整体二分好难a
CDQ分治&整体二分九连:
A[适者]
先来一个不是cdq分治的题(虽然也可以用分治做)
首先先来考虑按照什么顺序来搞掉这些机甲
对于两个相邻的机甲i,j,先i后j的代价是 Ai∗Di+(Di+Dj)∗Aj A i ∗ D i + ( D i + D j ) ∗ A j
那么如果i应该比j先消灭就有
Ai∗Di+(Di+Dj)∗Aj<Aj∗Dj+(Di+Dj)∗Ai A i ∗ D i + ( D i + D j ) ∗ A j < A j ∗ D j + ( D i + D j ) ∗ A i
也就是 Aj∗Di<Dj∗Ai A j ∗ D i < D j ∗ A i
所以将原序列按照 Di/Ai D i / A i 做关键字排序
让后考虑怎么用这两个大招
我们设 Ci C i 表示干掉第i个人的收益
那么可以得出 Ci=Ai∗(∑ij=1Dj)+Di∗(∑nj=i+1Aj) C i = A i ∗ ( ∑ j = 1 i D j ) + D i ∗ ( ∑ j = i + 1 n A j )
那么,同时干掉两个人i,j(i
#include
#include
#include
#include
#define db double
#define N 300010
#define eps 1e-6
#define LL long long
#define son(x) (s[f[x]][1]==x)
using namespace std;
struct p{ int d,t; } w[300010];
int n,A,q[N],s[N][2],f[N],rt,cnt; db l[N],r[N];
LL ans,C[N],S[N],T[N],D[N],SD[N];
inline bool c1(p a,p b){
return a.t*b.dinline db slp(int j,int k){
if(T[j]==T[k]) return C[k]>C[j]?1e8:-1e8;
return (C[j]-C[k])/(db)(T[j]-T[k]);
}
inline void rot(int x){
int p=f[x],g=f[p],d=son(x);
s[p][d]=s[x][!d]; f[s[p][d]]=p;
if(g) s[g][son(p)]=x; f[x]=g;
s[x][!d]=p; f[p]=x;
}
inline void splay(int x,int r=0){
if(!x) return;
for(int p;(p=f[x])!=r;rot(x))
if(f[p]!=r && son(x)==son(p)) rot(p);
if(!r) rt=x;
}
inline void insert(int k){
if(!rt){ rt=k; return; }
for(int x=rt;;){
int d=T[x]if(!s[x][d]){ s[x][d]=k; f[k]=x; splay(k); return; }
x=s[x][d];
}
}
inline int gPre(int k){
int r=0;
for(int x=s[k][0];x;){
if(slp(x,k)<=l[x]+eps) r=x,x=s[x][1];
else x=s[x][0];
}
return r;
}
inline int gSuc(int k){
int l=0;
for(int x=s[k][1];x;){
if(slp(x,k)+eps>=r[x]) l=x,x=s[x][0];
else x=s[x][1];
}
return l;
}
inline void ps(int k){
int p;
if(s[k][0]){
p=gPre(k);
splay(p,k);
f[s[p][1]]=0;
s[p][1]=0;
l[k]=r[p]=slp(k,p);
} else l[k]=1e9;
if(s[k][1]){
p=gSuc(k);
splay(p,k);
f[s[p][0]]=0;
s[p][0]=0;
r[k]=l[p]=slp(k,p);
} else r[k]=-1e9;
}
inline void maintain(int k){
splay(k); ps(k); int p;
if(l[k]<=r[k]){
if(!s[k][0] || !s[k][1]){
rt=s[k][0]+s[k][1];
f[rt]=0; ps(rt);
} else {
p=gSuc(k);
splay(p,k);
rt=s[k][0];
f[rt]=0;
s[rt][1]=p;
f[p]=rt;
ps(p); ps(rt);
}
}
}
inline int find(db k){
if(!rt) return 0;
for(int x=rt;x;){
if(r[x]>k+eps) x=s[x][1];
else if(l[x]+eps0];
else return x;
}
}
int main(){
scanf("%d%d",&n,&A);
for(int i=1;i<=n;++i){
scanf("%d%d",&w[i].d,&w[i].t);
w[i].t=w[i].t/A+(w[i].t%A>0);
}
sort(w+1,w+1+n,c1); *S=-1;
for(int i=1;i<=n;++i){
T[i]=w[i].t;
D[i]=w[i].d;
S[i]=S[i-1]+T[i];
C[i]=S[i]*D[i];
}
LL SS=0;
for(int i=1;i<=n;++i) SS+=C[i];
for(int i=n;i;--i){
SD[i]=SD[i+1]+D[i];
C[i]+=SD[i+1]*T[i];
}
insert(1); maintain(1);
for(int i=2;i<=n;++i){
int j=find(D[i]);
ans=max(ans,C[i]+C[j]-D[i]*T[j]);
insert(i); maintain(i);
}
printf("%lld\n",SS-ans);
}
B[陌上花开]
正文开始,顺便口胡cdq分治的原理
题意大概就是你有几个限制让后求满足的(i
首先我们将所有的花按照 ai a i 排序,那么就只有前面i的能为后面的j给出贡献
让后,如果没有 ci c i 这个限制就可以用树状数组统计 bi<bj b i < b j 的个数了
现在加上c的限制,我们考虑分治
将整个序列分为[l,mid]和[mid+1,r]
让后分别按照b作为关键字排序
接下来,类似归并排序的操作,我们将这两个部分重新合并到一起,设i,j分别为两个部分未处理的第一个元素
若 bi<bj b i < b j 我们将 ci c i 加入BIT,否则我们询问BIT中有多少个小于 cj c j 的并加入答案
注意到我们这样打乱了原来按照a排序的顺序,所以我们先递归到两个子区间去处理答案,这样处理完之后答案就自然是按照b有序的了,也省掉了排序这个步骤了
最后注意全等的情况
#include
#include
#include
#define N 100010
using namespace std;
int n,k,w[N<<1],A[N],c[N];
struct fl{ int a,b,c,r; } a[N],b[N];
inline bool c1(fl a,fl b){
return a.a==b.a?(a.b==b.b?(a.c==b.c?a.rq(int l,int r){
if(l==r) return;
int m=l+r>>1,i=l,j=m+1,t=l;
cdq(l,m); cdq(m+1,r);
for(;i<=m && j<=r;){
if(a[i].b<=a[j].b){
for(int x=a[i].c;x<=k;x+=x&-x) ++w[x];
b[t++]=a[i++];
} else {
for(int x=a[j].c;x;x&=x-1) A[a[j].r]+=w[x];
b[t++]=a[j++];
}
}
for(;i<=m;) b[t++]=a[i++];
for(;j<=r;){
for(int x=a[j].c;x;x&=x-1) A[a[j].r]+=w[x];
b[t++]=a[j++];
}
for(;l<=r;++l){
for(int x=b[l].c;x<=k;x+=x&-x) w[x]=0;
a[l]=b[l];
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),a[i].r=i;
sort(a+1,a+1+n,c1);
cdq(1,n);
sort(a+1,a+1+n,c1);
for(int i=n-1;i;--i) if(a[i].a==a[i+1].a &&a[i].b==a[i+1].b && a[i].c==a[i+1].c){
A[a[i].r]=max(A[a[i].r],A[a[i+1].r]);
}
for(int i=1;i<=n;++i) ++c[A[i]];
for(int i=0;iprintf("%d\n",c[i]);
}
C[拦截导弹]
就一个LIS做完啦
不光是二维LIS还要计算方案还不取模真不知道出题人想干什么
还好数据比较水double就存下了
首先要知道在LIS中出现的条件是什么
我们设 fi f i 表示以i结尾的LIS的长度,那么转移很简单 fi=max(fj+1),vj<vi,hj<hi f i = m a x ( f j + 1 ) , v j < v i , h j < h i
让后我们将整个序列取反并翻转做一次LIS就得到了 f′i f i ′
如果有 fi+f′i=|LIS|+1 f i + f i ′ = | L I S | + 1 那么i就可以出现在LIS之中
另外,i被拦截的概率就是通过i的方案数/总方案数
我们设 gi,g′i g i , g i ′ 表示正着做和倒着做的方案数,那么概率就是 p=gi∗g′i∑fi=|LIS|gi p = g i ∗ g i ′ ∑ f i = | L I S | g i
这时候我们又面临一个选择
1.树套树维护f和g
2.cdq分治
那肯定选分治啦,树套树哪里比得上cdq分治2333
#include
#include
#include
#define N 50010
#define db double
using namespace std;
int n,m,w[N],T; db W[N];
struct P{ int v,h,r,f[2]; db g[2]; } s[N],b[N];
inline bool cv(P a,P b){ return a.v>b.v; }
inline int gmx(int& x,int y){ return x<y?(x=y)|1:0; }
inline void add(P s){
int f=s.f[T]; db g=s.g[T];
for(int x=s.h;x<=m;x+=x&-x)
if(gmx(w[x],f)) W[x]=g;
else if(w[x]==f) W[x]+=g;
}
inline void query(P& s){
int& f=s.f[T]; db& g=s.g[T];
for(int x=s.h;x;x&=x-1)
if(w[x]) if(gmx(f,w[x]+1)) g=W[x];
else if(w[x]+1==f) g+=W[x];
}
inline void clr(int x){
for(;x<=m;x+=x&-x) W[x]=w[x]=0;
}
inline void cdq(int l,int r){
if(l==r){ if(gmx(s[l].f[T],1)) s[l].g[T]=1; return; }
int m=l+r>>1,i,j,t;
memcpy(b+l,s+l,(r-l+1)*sizeof(P));
for(t=l,i=l,j=m+1;t<=r;)
if(b[t].r<=m) s[i++]=b[t++];
else s[j++]=b[t++];
cdq(l,m);
for(t=i=l,j=m+1;i<=m && j<=r;)
if(s[i].v>=s[j].v) add(s[i++]);else query(s[j++]);
for(;j<=r;) query(s[j++]);
for(i=l;i<=m;++i) clr(s[i].h);
cdq(m+1,r);
for(t=i=l,j=m+1;i<=m && j<=r;)
if(s[i].v>=s[j].v) b[t++]=s[i++]; else b[t++]=s[j++];
for(;i<=m;) b[t++]=s[i++];
for(;j<=r;) b[t++]=s[j++];
for(;l<=r;++l) s[l]=b[l];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d%d",&s[i].v,&s[i].h),s[i].r=i;
for(int i=1;i<=n;++i) w[i]=s[i].h;
sort(w+1,w+1+n); m=unique(w+1,w+1+n)-w-1;
for(int i=1;i<=n;++i) s[i].h=m-(lower_bound(w+1,w+1+m,s[i].h)-w)+1;
sort(s+1,s+1+n,cv); memset(w,0,sizeof w);
cdq(1,n);
for(int i=1;i<=n;++i){
s[i].r=n-s[i].r+1;
s[i].h=m-s[i].h+1; s[i].v=-s[i].v;
}
sort(s+1,s+1+n,cv); ++T;
cdq(1,n); db S=0; int A=0;
for(int i=1;i<=n;++i)
if(gmx(A,s[i].f[0])) S=s[i].g[0];
else if(A==s[i].f[0]) S+=s[i].g[0];
printf("%d\n",A);
for(int i=1;i<=n;++i)
W[n-s[i].r+1]=(s[i].f[0]+s[i].f[1]==A+1?s[i].g[0]*s[i].g[1]/S:0);
for(int i=1;i<=n;++i) printf("%lf ",W[i]);
}
D[教义问答手册]
画风突变,变成了纯分治
先令 s[i]=∑Lj=1b[i+j] s [ i ] = ∑ j = 1 L b [ i + j ] 那么问题就变成了选择若干个元素距离>=L
我们先考虑怎么在线做,有两个选择
1.线段树,设每个点存 fi,j f i , j 表示去掉左边i个和右边j个的最大答案,那么可以搞一个转移 fi,j=max{fi,k+f′j,L−1−k},0<=k<=L−1 f i , j = m a x { f i , k + f j , L − 1 − k ′ } , 0 <= k <= L − 1
但是这个复杂度爆炸了 O(nL3) O ( n L 3 ) 所以不行
2.分块,反正也不太行
好的我们开始借鉴一下线段树的做法,考虑分治
我们发现,原来那个O(L^3)的dp可以被化简到O(L)
考虑处理经过中点的询问,我们搞一个 fi,j f i , j 距离中点距离为i,做到j这个位置的最大答案,对左边和右边分别从中点做一次这个dp,那么右边部分转移就是 fj=max{fj−1,fj−L+s[j]} f j = m a x { f j − 1 , f j − L + s [ j ] } ,左边也是类似的
那么对于一个询问[l,r]就是 max{fk,l+fL−k−1,r},0<=k<=L m a x { f k , l + f L − k − 1 , r } , 0 <= k <= L
这样就可以做到 O(L∗n logn) O ( L ∗ n l o g n ) 了
#include
#include
#include
#define N 200010
using namespace std;
struct opt{ int l,r,i; } g[N],b[N];
int n,m,c,L,s[N],v[N],f[50][N],A[N];
inline void cdq(int l,int r,int n){
if(l==r) return;
int m=l+r>>1,i,j;
for(int T=0;Tint*F=f[T];
F[j=m+1+T]=max(0,s[j]);
for(++j;j<=m+T+L&&j<=r;++j) F[j]=max(F[j-1],s[j]);
for(;j<=r;++j) F[j]=max(F[j-1],F[j-L]+s[j]);
F[i=m-T]=max(0,s[i]);
for(--i;i>m-T-L&&i>=l;--i) F[i]=max(F[i+1],s[i]);
for(;i>=l;--i) F[i]=max(F[i+1],F[i+L]+s[i]);
}
for(i=1;i<=n;++i){
if(g[i].l<=m&&m0][g[i].l],f[0][g[i].r]);
for(int T=0;Tif(T<=m-g[i].l&&L-T<=g[i].r-m)
A[g[i].i]=max(A[g[i].i],f[T][g[i].l]+f[L-T-1][g[i].r]);
swap(g[i--],g[n--]);
}
}
for(i=1,j=n;i<=j;++i) if(g[i].r>m) swap(g[i--],g[j--]);
cdq(l,m,j);
for(i=1,j=n;i<=j;++i) if(g[i].l<=m) swap(g[i--],g[j--]);
cdq(m+1,r,j);
}
int main(){
scanf("%d%d",&n,&L);
for(int i=1;i<=n;++i){
scanf("%d",s+i); s[i]+=s[i-1];
}
for(int i=n;i>=L;--i) s[i]-=s[i-L];
scanf("%d",&m);
for(int l,r,i=1;i<=m;++i){
scanf("%d%d",&l,&r); l+=L-1;
if(r-l+1>L) g[++c]=(opt){l,r,i};
else for(int j=l;j<=r;++j) A[i]=max(A[i],s[j]);
}
cdq(L,n,c);
for(int i=1;i<=m;++i) printf("%d\n",A[i]);
}
E[二分图]
新的题目,新的套路
首先二分图的性质:没有长度为奇数的环
维护这个性质我们使用带权并查集,首先一开始整个图一定是个森林
考虑加入一条边所带来的影响,设v[x]表示x到root[x]所经过的边数的奇偶性
那么两个点a,b的距离就可以用v[a]^v[b]表示
如果v[a]^v[b]是奇数,那么加入边(a,b)就形成了奇环,否则我们可以不加入这条边,因为它不影响构成奇环
那么加上删除操作,我们需要分治,具体就是将这些边的时间视为线段树上的一个区间,让后对每个时间节点进行操作,如果覆盖了整个节点,那么我们将这条边加入并查集,否则递归下去,注意到这里有需要撤销的操作,我们可以用可持久化,但是这里只需要暴力撤销+不要路径压缩就可以了,复杂度 O(nlog2n) O ( n l o g 2 n )
#include
#include
#include
#define N 200010
using namespace std;
struct edge{ int u,v,s,t; } G[N<<1];
int n,m,T,f[N],v[N],s[N<<2],r[N],t;
inline int g(int x,int& y,int r=0){
for(y=x;y!=f[y];y=f[y]) r^=v[y];
return r;
}
inline void merge(int x,int y,int d){
if(r[x]>r[y]) swap(x,y);
if(r[x]==r[y]){ ++r[y]; s[++t]=-y; }
f[x]=y; s[++t]=x; v[x]=d;
}
inline void back(int dt){
for(;t>dt;--t)
s[t]<0?--r[-s[t]]:f[s[t]]=s[t],v[s[t]]=0;
}
inline void cdq(int l,int r,int n){
int m=l+r>>1,i,j,dt=t;
for(i=1;i<=n;++i){
if(G[i].s<=l && r<=G[i].t){
int x,y,d=g(G[i].u,x)^g(G[i].v,y)^1;
if(x!=y) merge(x,y,d);
else if(d){
for(;l<=r;++l) puts("No");
back(dt); return;
}
swap(G[i--],G[n--]);
}
}
if(l==r){ puts("Yes"); back(dt); return; }
for(i=1,j=n;i<=j;++i)
if(G[i].s>m) swap(G[i--],G[j--]);
cdq(l,m,j);
for(i=1,j=n;i<=j;++i)
if(G[i].t<=m) swap(G[i--],G[j--]);
cdq(m+1,r,j);
back(dt);
}
int main(){
scanf("%d%d%d",&n,&m,&T);
for(int i=1;i<=n;++i) f[i]=i;
for(int i=1;i<=m;++i){
scanf("%d%d%d%d",&G[i].u,&G[i].v,&G[i].s,&G[i].t);
if(++G[i].s>G[i].t){ --m; --i; }
}
cdq(1,T,m);
}
F[Shortest Path Queries]
和上面的题目套路一样辣
注意到题目要求的是异或最短路
那么我们考虑一下怎么做,先搞个dfs树出来,加上返祖边就得到了很多环
令 vx=vfx xor w(fx,x) v x = v f x x o r w ( f x , x ) ,我们发现这个最短路其实就是 vx xor vy v x x o r v y 再异或上若干个环的权值,因为走到环上和走回来的权值抵消了,那么就可以愉快地使用线性基了
#include
#include
#include
#include
#define N 400010
using namespace std;
mapint ,int>,int> S;
struct Q{ int x,y; } g[N<<2];
struct edge{ int u,v,c,s,t; } G[N<<2];
int n,m,cnt,qc,qn,clk,f[N],v[N],s[N<<2],t,r[N];
struct Lbase{
int p[31];
Lbase(){ memset(p,0,sizeof p); }
inline void add(int x){
for(int i=30;~i;--i)
if(x&(1<if(!p[i]){ p[i]=x; return; }
else x^=p[i];
}
inline int query(int x){
for(int i=30;~i;--i) if(x>(x^p[i])) x^=p[i];
return x;
}
} Z;
inline int gf(int x,int& y,int r=0){
for(y=x;y!=f[y];y=f[y]) r^=v[y];
return r;
}
inline void merge(int x,int y,int d){
if(r[x]>r[y]) swap(x,y);
if(r[x]==r[y]){ ++r[y]; s[++t]=-y; }
f[x]=y; s[++t]=x; v[x]=d;
}
inline void back(int dt){
for(;t>dt;--t)
s[t]<0?--r[-s[t]]:f[s[t]]=s[t],v[s[t]]=0;
}
inline void cdq(int l,int r,int n,Lbase B){
int dt=t,m=l+r>>1,i,j;
for(int i=1;i<=n;++i)
if(G[i].s<=l && r<=G[i].t){
int x,y,d=gf(G[i].u,x)^gf(G[i].v,y);
if(x!=y) merge(x,y,G[i].c^d);
else B.add(d^G[i].c);
swap(G[i--],G[n--]);
}
if(l==r){
printf("%d\n",B.query(gf(g[l].x,j)^gf(g[l].y,j)));
back(dt); return;
}
for(i=1,j=n;i<=j;++i)
if(G[i].s>m) swap(G[i--],G[j--]);
cdq(l,m,j,B);
for(i=1,j=n;i<=j;++i)
if(G[i].t<=m) swap(G[i--],G[j--]);
cdq(m+1,r,j,B); back(dt);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) f[i]=i;
for(int x,y,c,i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&c);
G[++cnt]=(edge){x,y,c,1,-1};
S[make_pair(x,y)]=cnt;
}
scanf("%d",&m);
for(int o,x,y,c,i=1;i<=m;++i){
scanf("%d%d%d",&o,&x,&y);
if(o==1){
scanf("%d",&c);
G[++cnt]=(edge){x,y,c,clk+1,-1};
S[make_pair(x,y)]=cnt;
} else if(o==2){
G[S[make_pair(x,y)]].t=clk;
S.erase(make_pair(x,y));
} else{ g[++clk]=(Q){x,y}; }
}
for(int i=1;i<=cnt;++i) if(!~G[i].t) G[i].t=clk;
cdq(1,clk,cnt,Z);
}
G[Radio stations]
画风又变回来了, ri r i 范围巨大+需要数值运算强制不给你离散化只好分治辣
#include
#include
#include
#define N 100010
using namespace std;
int n,k,m=0,x[N],f[N],r[N],w[N],V,sa[N]; long long ans=0;
struct opt{
int o,x,l;
} s[N<<2],b[N<<2];
inline bool c1(int i,int j){ return r[i]>r[j]; }
inline void cdq(int l,int r){
if(l==r) return;
int m=l+r>>1,i=l,j=m+1,t=l;
cdq(l,m);
cdq(m+1,r);
for(int c;i<=m && j<=r;){
if(s[i].x<=s[j].x){
if(!s[i].o)
for(int x=s[i].l;x<=V;x+=x&-x) ++w[x];
b[t++]=s[i++];
} else {
if(s[j].o){
c=0;
for(int x=min(V,s[j].l+k);x;x&=x-1) c+=w[x];
for(int x=max(0,s[j].l-k-1);x;x&=x-1) c-=w[x];
ans+=(s[j].o==2?c:-c);
}
b[t++]=s[j++];
}
}
for(;i<=m;) b[t++]=s[i++];
for(int c;j<=r;){
if(s[j].o){
c=0;
for(int x=min(V,s[j].l+k);x;x&=x-1) c+=w[x];
for(int x=max(0,s[j].l-k-1);x;x&=x-1) c-=w[x];
ans+=(s[j].o==2?c:-c);
}
b[t++]=s[j++];
}
for(;l<=r;++l){
if(!s[l].o) for(int x=s[l].l;x<=V;x+=x&-x) w[x]=0;
s[l]=b[l];
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%d%d%d",x+i,r+i,f+i),sa[i]=i;
sort(sa+1,sa+1+n,c1);
for(int i,j=1;j<=n;++j){ i=sa[j];
s[++m]=(opt){0,x[i],f[i]}; V=max(V,f[i]);
s[++m]=(opt){1,x[i]-r[i]-1,f[i]};
s[++m]=(opt){2,x[i]+r[i],f[i]};
}
cdq(1,m);
printf("%lld\n",ans-n);
}
H[k大数查询]
画风突变为整体二分
好难啊不会啊打个树套树水过去吧
外面权值,里面区间加打个标记就可以了
求答案就在外面二分,询问[l,r]的元素个数而已
#include
#include
#include
#define N 50010
#define LL long long
#define mid (l+r>>1)
using namespace std;
int n,m,cnt=0,rt[N<<3];
struct tree{ int l,r,t; LL s; } s[N*400];
inline void add(int l,int r,int& x,int L,int R){
if(!x) x=++cnt;
if(L<=l && r<=R){ s[x].t++; s[x].s+=r-l+1; return; }
if(L<=mid) add(l,mid,s[x].l,L,R);
if(mid1,r,s[x].r,L,R);
s[x].s=s[s[x].l].s+s[s[x].r].s+s[x].t*(r-l+1);
}
inline LL sum(int l,int r,int& x,int L,int R,int T=0){
if(!x) x=++cnt;
if(L<=l && r<=R) return s[x].s+T*(r-l+1ll);
int A=0;
if(L<=mid) A+=sum(l,mid,s[x].l,L,R,T+s[x].t);
if(mid1,r,s[x].r,L,R,T+s[x].t);
return A;
}
inline void update(int l,int r,int x,int L,int R,int c){
add(1,n,rt[x],L,R);
if(l==r) return;
if(c<=mid) update(l,mid,x<<1,L,R,c);
else update(mid+1,r,x<<1|1,L,R,c);
}
inline int query(int l,int r,int x,int L,int R,int k){
if(l==r) return l;
LL c=sum(1,n,rt[x<<1|1],L,R);
if(c>=k) return query(mid+1,r,x<<1|1,L,R,k);
else return query(l,mid,x<<1,L,R,k-c);
}
int main(){
scanf("%d%d",&n,&m);
for(int o,l,r,c;m--;){
scanf("%d%d%d%d",&o,&l,&r,&c);
if(o==1) update(0,n,1,l,r,c);
else printf("%d\n",query(0,n,1,l,r,c));
}
}
I[网络管理network]
树上第k大?很简单啊树剖套BIT套主席树,四个log随便过
考虑静态的做法,每个节点维护它到根的权值线段树,那么询问就可以二分了
单独考虑修改,我们先求出dfs序,让后一次修改相当于整个子树的加减操作,询问只是一个点询问,那么搞一个BIT套主席树就可以了,复杂度直接降到了2个log
#include
#include
#include
#define N 80010
#define mid (l+r>>1)
using namespace std;
int n,m,q,w[N<<1],h[N],rt[N],cnt,v[N],clk,V[N];
int f[N],d[N],top[N],son[N],sz[N],l[N],r[N],o[N][3];
struct edge{ int v,nt; } G[N<<1];
struct tree{ int l,r,s; } s[N<<7];
inline void copy(int l,int r,int r1,int& r2,int k){
if(!r2) r2=++cnt; s[r2].s=s[r1].s+1;
if(l==r) return;
if(k<=mid){ s[r2].r=s[r1].r; copy(l,mid,s[r1].l,s[r2].l,k); }
else{ s[r2].l=s[r1].l; copy(mid+1,r,s[r1].r,s[r2].r,k); }
}
inline void insert(int l,int r,int& x,int k,int c){
if(!x) x=++cnt; s[x].s+=c;
if(l==r) return;
if(k<=mid) insert(l,mid,s[x].l,k,c);
else insert(mid+1,r,s[x].r,k,c);
}
struct fenwick{
int w[N];
inline void add(int x,int v,int c){
for(;x<=n;x+=x&-x) insert(1,m,w[x],v,c);
}
inline void query(int x,int* t){
if(x) t[++*t]=rt[x]; x=l[x];
for(;x;x&=x-1) if(w[x]) t[++*t]=w[x];
}
} T;
inline void dfs(int x,int p){
f[x]=p; d[x]=d[p]+1; sz[x]=1;
for(int v,i=h[x];i;i=G[i].nt)
if(!d[v=G[i].v]){
dfs(v,x);
sz[x]+=sz[v];
if(sz[son[x]]inline void dgs(int x,int p){
l[x]=++clk; top[x]=p;
copy(1,m,rt[f[x]],rt[x],v[x]);
if(son[x]) dgs(son[x],p);
for(int v,i=h[x];i;i=G[i].nt)
if(!l[v=G[i].v]) dgs(v,v);
r[x]=clk;
}
inline int gLca(int x,int y){
for(;top[x]!=top[y];y=f[top[y]])
if(d[top[x]]>d[top[y]]) swap(x,y);
return d[x]>d[y]?y:x;
}
int A[140],B[140];
inline int query(int l,int r,int k){
if(l==r) return l;
int C=0;
for(int i=1;i<=*A;++i) C+=s[s[A[i]].r].s;
for(int i=1;i<=*B;++i) C-=s[s[B[i]].r].s;
if(C>=k){
for(int i=1;i<=*A;++i) A[i]=s[A[i]].r;
for(int i=1;i<=*B;++i) B[i]=s[B[i]].r;
return query(mid+1,r,k);
} else {
for(int i=1;i<=*A;++i) A[i]=s[A[i]].l;
for(int i=1;i<=*B;++i) B[i]=s[B[i]].l;
return query(l,mid,k-C);
}
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i) scanf("%d",v+i);
memcpy(w+1,v+1,n<<2);
for(int x,y,i=1;iscanf("%d%d",&x,&y);
G[++m]=(edge){y,h[x]}; h[x]=m;
G[++m]=(edge){x,h[y]}; h[y]=m;
}
m=n;
for(int i=1;i<=q;++i){
scanf("%d%d%d",o[i],o[i]+1,o[i]+2);
if(!o[i][0]) w[++m]=o[i][2];
}
sort(w+1,w+1+m); m=unique(w+1,w+1+m)-w-1;
for(int i=1;i<=n;++i) v[i]=lower_bound(w+1,w+1+m,v[i])-w;
for(int i=1;i<=q;++i) if(!o[i][0]) o[i][2]=lower_bound(w+1,w+1+m,o[i][2])-w;
dfs(1,0);
dgs(1,1);
for(int x,y,t,i=1;i<=q;++i){
if(!o[i][0]){
x=o[i][1];
T.add(l[x],v[x],-1);
T.add(r[x]+1,v[x],1);
v[x]=o[i][2];
T.add(l[x],v[x],1);
T.add(r[x]+1,v[x],-1);
} else {
x=o[i][1]; y=o[i][2]; t=gLca(x,y);
if(d[x]+d[y]-d[t]-d[f[t]]0]){
puts("invalid request!");
continue;
}
*A=*B=0;
T.query(x,A);
T.query(y,A);
T.query(t,B);
T.query(f[t],B);
printf("%d\n",w[query(1,m,o[i][0])]);
}
}
}