BZOJ 4867 分块+神tm卡常

思路:

注意到len<=10

按照权值max-min<=sqrt(n)*len 分块

记一下前缀和  每修改sqrt(n)次以后重新分块

修改的时候整块打标记  两边重构

(这题常数卡得要死   找同学要来fread才过)

查询的时候 就 二分答案 O(sqrt(n))判断

二分的上界要实时根据maxdeep变化才能过

//By SiriusRen
#include<bits/stdc++.h>
using namespace std;
const int N=100050;
int n,m,op,xx,yy,len,limval,Block,deep[N],cnt,dfn[N],lst[N],maxdep;
int first[N],next[N],v[N],w[N],tot,a[N],pos[N],maxx[N],minn[N],L[N],R[N],tag[N];
unsigned short sum[1200][33333];
inline int nextChr() {
    static const int siz=1<<22;
    static char buf[siz],*chr=buf+siz;
    if(chr==buf+siz)fread(chr=buf,1,siz,stdin);
    return int(*chr++);
}
inline int read(){
    register int r=0,c=nextChr();
    for(;c<48;c=nextChr());
    for(;c>47;c=nextChr())r=(r<<3)+(r<<1)+c-48;
    return r;
}
inline void add(int x,int y,int z){w[tot]=z,v[tot]=y,next[tot]=first[x],first[x]=tot++;}
void dfs(int x){
    dfn[x]=++cnt,a[cnt]=deep[x];
    for(int i=first[x];~i;i=next[i])
        deep[v[i]]=deep[x]+w[i],dfs(v[i]);
    lst[x]=cnt;
}
void rebuild(int x){
    for(int i=0;i<=limval;i++)sum[x][i]=0;maxx[x]=0;
    for(int i=L[x];i<=R[x];i++)sum[x][a[i]]++,maxx[x]=max(maxx[x],a[i]);
    for(int i=1;i<=maxx[x];i++)sum[x][i]+=sum[x][i-1];
}
void build(){
    int bs=0;limval=n*len/Block;maxdep=0;
    for(int i=1;i<=n;i++)a[i]=a[i]+tag[pos[i]],maxdep=max(maxdep,a[i]);
    for(int i=1;i<=n;){
        bs++,L[bs]=R[bs]=i,maxx[bs]=minn[bs]=a[i];
        while(i<=n&&max(maxx[bs],a[i])-min(minn[bs],a[i])<=limval&&R[bs]-L[bs]<=Block)
            R[bs]=i,pos[i]=bs,maxx[bs]=max(maxx[bs],a[i]),minn[bs]=min(minn[bs],a[i]),i++;
    }
    for(int i=1;i<=bs;i++){
        maxx[i]-=(tag[i]=minn[i]);
        for(int j=L[i];j<=R[i];j++)a[j]-=tag[i];
        rebuild(i);
    }
}
void change(int l,int r,int wei){
    if(pos[l]==pos[r]){for(int i=l;i<=r;i++)a[i]+=wei;rebuild(pos[l]);}
    else{
        for(int i=l;i<=R[pos[l]];i++)a[i]+=wei;rebuild(pos[l]);
        for(int i=L[pos[r]];i<=r;i++)a[i]+=wei;rebuild(pos[r]);
        for(int i=pos[l]+1;i<=pos[r]-1;i++)tag[i]+=wei;
    }
}
int kth(int l,int r,int wei){
    int ans=0;
    if(pos[l]==pos[r]){for(int i=l;i<=r;i++)ans+=(tag[pos[l]]+a[i]<=wei);}
    else{
        for(int i=l;i<=R[pos[l]];i++)ans+=(tag[pos[l]]+a[i]<=wei);
        for(int i=L[pos[r]];i<=r;i++)ans+=(tag[pos[r]]+a[i]<=wei);
        for(int i=pos[l]+1;i<=pos[r]-1;i++)if(wei>=tag[i])
            ans+=sum[i][min(maxx[i],wei-tag[i])];
    }return ans;
}
int bsrch(int l,int r,int k){
    if(r-l+1<k)return -1;
    int lft=0,rit=maxdep,ans=-1;
    while(lft<=rit){
        int mid=(lft+rit)>>1;
        if(kth(l,r,mid-1)+1<=k)lft=mid+1,ans=mid;
        else rit=mid-1;
    }return ans;
}
int main(){
    memset(first,-1,sizeof(first));
    n=read(),m=read(),len=read(),Block=min(n,(int)sqrt(n));
    for(int i=2;i<=n;i++)xx=read(),yy=read(),add(xx,i,yy);
    dfs(1),build();
    while(m--){
        if(cnt>Block)cnt=0,build();
        op=read(),xx=read(),yy=read();
        if(op==1)printf("%d\n",bsrch(dfn[xx],lst[xx],yy));
        else limval+=yy,maxdep+=yy,change(dfn[xx],lst[xx],yy),cnt++;
    }
}
时间: 2024-10-29 19:08:52

BZOJ 4867 分块+神tm卡常的相关文章

bzoj4028 [HEOI2015]公约数数列(分块+卡常?)

被卡常卡到怀疑人生. 思维又难又卡常(可能是我写的太丑了)心态炸了. 最后还是照题解打的.(题解多了一个排序,似乎快了很多) 所以代码就不发了... 原文地址:https://www.cnblogs.com/Xu-daxia/p/9465265.html

[luogu1972][bzoj1878][SDOI2009]HH的项链【莫队+玄学卡常】

题目大意 静态区间查询不同数的个数. 分析 好了,成功被这道题目拉低了AC率... 打了莫队T飞掉了,真的是飞掉了QwQ. 蒟蒻想不出主席树的做法,就换成了莫队... 很多人都不知道莫队是什么... 一句话概括莫队:离线询问分块排序,玄学降低复杂度 那么这道题目就是简单的莫队模板套一下就好了,每一次看看更新的点是不是会对答案造成贡献就可以过掉了. 但是复杂度很明显是\(Q(\sqrt{n}m)\),成功T掉,加上玄学卡常,破罐子破摔了100+终于过掉了. #include <bits/stdc+

Codeforces 988D Points and Powers of Two 【性质】【卡常】

这道题关键在于想到两个性质,想到就好做了.这还是我做过的第一道卡常题 1.满足题目中条件的子集,其中元素个数不能大于3 2.如果最大子集为3的话,那一定是x-2^i,  k, x+2^i的形式,我们枚举x就好了,然后i的次数是log10^9:如果最大子集是2,那就是x,x+2^i的形式,同样枚举x:如果最大子集是1,输出a[1]就行 整体复杂度是O(n*logn*log10^9) 1 #include<iostream> 2 #include<set> 3 using namesp

卡常模板

卡常模板,必背 1 #pragma GCC optimize(1) 2 #pragma GCC optimize(2) 3 #pragma GCC optimize(3) 4 #include<iostream> 5 #include<cstdio> 6 #include<ctime> 7 #include<cstdlib> 8 #include<cstring> 9 #include<algorithm> 10 #define Ri

论OI中各种玄学卡常

当你在写程序的时候一般出现过这种无比悲剧的情况: 你讨厌卡常?下面有二则小故事: 作为一个经常出题的人,其实很多时候出题时的画风是这样的:"我有一个绝妙的\(O(nlog^2n)\)的算法,我来出道题吧""咦怎么只能跑 \(5w\) 啊,好咸鱼啊,我要让它能跑 \(10w\),嗯现在 \(10w\) 只要 \(0.3s\) 了,要不努把力跑个 \(20w\) 吧"然后就没有然后了.. 开O2之后FFT会比不开快几倍? 不开\(O2\):\(NTT\)比\(FFT\)

[CSP-S模拟测试]:卡常题/b(基环树+DP)

题目描述 $ρ$有一个二分连通无向图,$X$方点.$Y$方点均为$n$个(编号为$1\sim n$).这个二分图比较特殊,每一个$Y$方点的度为$2$,一条黑色边,一条白色边.所有黑色边权值均为$a$,所有白色边权值均为$b$.选择一个$X$方点,代价为连接的所有边的权值之和.激活一个$Y$方点,需要选择至少一个与之相邻的$X$方点.现在,$ρ$想激活每个$Y$方点,他想知道最小的总代价.不过$ρ$很善良,他给你开了$O2$优化.这样你就不会被卡常了.当然,除非你真的连读入优化都不想写,或者常数

时间卡常技巧

以下内容出自: 时间卡常技巧 先放一句话镇场: 我觉得,卡常数的出题人都是xx,这违背了算法竞赛考察思路的初衷 ——LYD 推荐:论OI中各种玄学卡常 我们一般说的复杂度都是O(n)O(n^2)O(nlogn)是一个级别. 但是我们其实每一个步可能计算很多次,然后会乘上一个2*n,3*n,甚至10*n 我们都叫O(n) 这个乘上的数就是常数.有的时候,你(chu)自(ti)己(ren)的(sang)程(xin)序(bing)可(kuang)能(ka)常(chang)数(shu)太(qwq)大(Q

【bzoj3240 &amp;&amp; 洛谷P1397】矩阵游戏[NOI2013](矩阵乘法+卡常)

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3240 这道题其实有普通快速幂+费马小定理的解法……然而我太弱了,一开始只想到了矩阵乘法的方法. 首先定义两个矩阵: $ A_{1} = \begin{bmatrix} a & b \\ 0 & 1 \end{bmatrix} $ $ A_{2} = \begin{bmatrix} c & d \\ 0 & 1 \end{bmatrix} $ 于是我们就可以得到这样

bzoj 2821 分块

分块: 先预处理,将原序列分成长度为len的许多块,计算从第i块到第j块的答案,(可以做到O(n*n/len)). 每次询问时,将询问的区间分成三部分,:左边,中间,右边,中间是尽量大的一个块区间,其答案已经计算得到,左右两边加起来最多有2*len个元素,暴力计算其对答案的影响.O(q*len*f(n)),f(n)是暴力加入一个元素的代价. 这道题f(n)是log(n) 总的复杂度:f(n) = O( n*n/len + q*len*log(n) ), 当len = n*(q*log(n))-1