树上的Nim取石子游戏。
说白了就是,维护一棵树,支持:修改点权,询问一条路径上点权的xor值是否为零。
可以用树链剖分做。但是介于n和q都50w这么大感觉常数很DT。。
既然它只修改点的话,影响到的只是它这棵子树。那么很容易就想到了dfs序。这个子树就是连续一段。
先维护每个点dfs开始时和结束时的时间戳。修改的时候先在它自己的开始、结束位置上xor它自己变成零,然后再修改。
(x,y)路径上的xor值=query(x的开始) xor query(y的开始) xor lca(x,y)的点权。很好想通。LCA就倍增算一下好了。
没了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define YES *o++='Y',*o++='e',*o++='s',*o++='\n' #define NO *o++='N',*o++='o',*o++='\n' const int maxn=500100,maxm=1000100; int p[maxm],n1[maxm],h[maxn],begin[maxn],end[maxn],bit[maxm],st[maxn],a[maxn],n,ee=0,tot=0; int anc[maxn][20],dep[maxn]; char buf[25000000],*o=buf,*pt=buf; inline int getint(){ int s=0; while(*pt<'0'||*pt>'9')pt++; while(*pt>='0'&&*pt<='9')s=s*10+*pt++-48; return s; }inline char getop(){ while(*pt!='Q'&&*pt!='C') pt++; return *pt++; }inline void ae(int a,int b){ p[ee]=b; n1[ee]=h[a]; h[a]=ee++; p[ee]=a; n1[ee]=h[b]; h[b]=ee++; }inline void dfs(int s){ int top=0; begin[st[++top]=s]=dep[s]=++tot; while(top){ int u=st[top],f=0; for(int i=h[u];~i;i=n1[i]){ if(!~begin[p[i]]){ begin[p[i]]=++tot; anc[p[i]][0]=u; dep[p[i]]=dep[u]+1; st[++top]=p[i]; h[u]=n1[i]; f=1; break; } }if(!f){ end[u]=++tot; top--; } } }inline int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); int k=dep[u]-dep[v],j=0; while(k){ if(k&1) u=anc[u][j]; j++,k>>=1; }if(u==v) return u; for(int j=19;~j;j--) if(anc[u][j]!=anc[v][j]) u=anc[u][j],v=anc[v][j]; return anc[u][0]; }inline void modify(int i){ for(int x=begin[i];x<maxm;x+=x&-x) bit[x]^=a[i]; for(int x=end[i];x<maxm;x+=x&-x) bit[x]^=a[i]; }inline int ask(int x){ int r=0; for(;x;x-=x&-x) r^=bit[x]; return r; }int main(){ fread(buf,1,25000000,stdin); n=getint(); for(int i=1;i<=n;i++) a[i]=getint(); memset(h,-1,sizeof(h)); memset(begin,-1,sizeof(begin)); for(int i=1;i<n;i++) ae(getint(),getint()); dfs(1); for(int i=1;i<=n;i++) modify(i); for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) anc[i][j]=anc[anc[i][j-1]][j-1]; int q=getint(),x,y,z; while(q--){ if(getop()=='Q'){ x=getint();y=getint(); if(ask(begin[x])^ask(begin[y])^a[lca(x,y)]) YES; else NO; }else{ x=getint();y=getint(); modify(x); a[x]=y; modify(x); } }return fwrite(buf,1,o-buf,stdout),0; }
题意:统计有几个连续子段的中位数(A[1..k]从大到小排序后A[k/2向上取整])大于等于给定值m。
应该还是比较好搞的。
把大于等于m的标记为1。小于m的标记为-1。
我们就只需要统计连续子段和非负的个数。
树状数组搞即可。
(新本本好好用。呵呵呵。)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=210000,d=100010; int n,m,a[100010],bit[maxn+10]; void modify(int x){ for(;x<=maxn;x+=x&-x) bit[x]++; } int ask(int x){ int r=0; for(;x;x-=x&-x) r+=bit[x]; return r; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(a[i]>=m) a[i]=1; else a[i]=-1; } int sum=0; long long An=0; modify(d); for(int i=1;i<=n;i++){ sum+=a[i]; An+=1ll*ask(sum+d); modify(sum+d); } cout << An << endl; return 0; }
题意:给定n个点,m个询问。每次询问一个矩形内有几个点。坐标范围10^7。
一眼看上去就很水啊。。坐标肯定是要离散化的。。询问跟着一起离散化。。然后以y为第一关键字,x为第二关键字排序。树状数组统计每个点左下角有多少个点。然后加加减减。。
离散化的细节貌似很恶心。。调着调着就A了。。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; char buf[20000000],*pt=buf,*o=buf; inline int getint(){ int s=0,f=1; while((*pt!='-')&&(*pt<'0'||*pt>'9'))pt++; if(*pt=='-')f=-1,pt++; while(*pt>='0'&&*pt<='9')s=s*10+*pt++-48; return s*f; } inline void print(int x){ char str[30],*p=str; if(!x)*o++=48; else{ while(x) *p++=x%10+48,x/=10; while(p--!=str)*o++=*p;} } struct point{ int x,y,id; point(){ id = -1; }; point(int _x,int _y,int _id){ x = _x, y = _y, id = _id; } }p[2800010]; bool cmp(point a,point b){ if(a.y == b.y){ if(a.x == b.x) return a.id < b.id; else return a.x < b.x; } return a.y < b.y; } int n,m,N,P,pc = 0; int X[810000],Y[810000],bx[810000],by[810000],bit[2800000],cnt[2800000]; void upd(int x){ for(;x <= N;x += x & -x) bit[x] ++; } int query(int x){ int r = 0; for(;x;x -= x & -x) r += bit[x]; return r; } int main(){ fread(buf,1,20000000,stdin); n = getint(); m = getint(); bx[0] = by[0] = 0; N = n + 1; P = (m << 2) + n; bx[N] = by[N] = 12345678; for(int i = 1;i <= n;i ++){ X[i] = getint(); Y[i] = getint(); bx[i] = ++ X[i]; by[i] = ++ Y[i]; } sort(bx + 1, bx + n + 1); sort(by + 1, by + n + 1); int cx = unique(bx , bx + n + 2) - bx -1; int cy = unique(by , by + n + 2) - by -1; for(int i = 1;i <= n;i ++){ p[(m << 2) + i - 1].x = upper_bound(bx,bx + cx,X[i]) - bx; p[(m << 2) + i - 1].y = upper_bound(by,by + cy,Y[i]) - by; } int x1,y1,x2,y2,X1,X2,X3,Y1,Y2,Y3; for(int i = 1;i <= m;i ++){ x1= getint(); y1=getint();x2=getint();y2=getint(); X1 = upper_bound(bx,bx + cx,x1) - bx; X2 = upper_bound(bx,bx + cx,x2) - bx; X3 = upper_bound(bx,bx + cx,x2 + 1) - bx; Y1 = upper_bound(by,by + cy,y1) - by; Y2 = upper_bound(by,by + cy,y2) - by; Y3 = upper_bound(by,by + cy,y2 + 1) - by; p[pc] = point(X3,Y3,pc ++); p[pc] = point(X1,Y3,pc ++); p[pc] = point(X3,Y1,pc ++); p[pc] = point(X1,Y1,pc ++); } sort(p,p + P,cmp); for(int i = 0;i < P;i ++){ if(~ p[i].id) cnt[p[i].id] = query(p[i].x); else upd(p[i].x); } for(int i = 1;i <= m;i ++) print(cnt[i - 1 << 2] - cnt[(i - 1 << 2) + 1] - cnt[(i - 1 << 2) + 2] + cnt[(i - 1 << 2) + 3]),*o++='\n'; return fwrite(buf,1,o-buf,stdout),0; }
分类讨论大丈夫!!!WTF!!!
题意:
1.给你不等式ax>b
2.删除第i条插入的不等式
3.询问x=k时,满足多少个给定的不等式。
n的范围10w,k的绝对值范围是100w。
那么我们可以先解出这个不等式,把符合这个不等式的所有数所在的位置加一。
比如解出来x>1,就把2~100w全都加一。x<1,就把-100w~0全都加一。
那么明显就是修改区间,单点询问。可以用线段树很好的完成。
但是不知道为什么看到100w就后怕写线段树要玩常数。于是选择了树状数组。
用树状数组的话就需要差分。比如[l,r]都加一,那么在l的位置上加一,r+1的位置上减一。这个很基础。
负数的话下标怎么办呢?设置一个偏移量d=100w+1。修改第x个时,在第x+d个位置上修改。保证下标都是正的方便操作。
看来这道题已经没什么问题了。那我们去写写看?好的。
【以下为吐槽,看tips请跳过若干个自然段】
【四天前】树状数组两行秒。ax>b自以为是的除一除就算解好了。样例很快就过了。很自信的交上去。RE还是WA来着。。然后手忙脚乱改一改还是WA。。这个时候想到了对拍。网上搞了个标程拍一拍。。不拍不知道一拍吓一跳。。然后我就被拍扁重建了。(这是替罪羊树?
发现我解ax>b的方法很不科学。感觉都没考虑全。后来直接去玩double。结果精度萎出翔。。
然后我cave in了。。
【今天】上课的时候我重新想了想ax>b该怎么解。考虑a、b的正负,发现四种情况都列出来。四个if即可。最前面再特判一下a=0就没问题了。回来信心满满的写。
造了组大数据对拍过了。很高心。结果还是WA。真是要吐血。后来突然想到,万一它要删除已经删除掉的呢。那我不就挂了吗。于是我再开了个数组记录。这回,过了。。。。。
【吐槽完了】
几个tips:
1.a=0要特判
2.ax>b分四种情况讨论(我是这么做的保险,或者有更神的方法,可惜我不会
3.如果x解出来不在-100w~100w之间要特判
4.删除已经删除的不等式直接continue!!!
贴代码。本人放个r1的代码攒rp好了~
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int d=1000001,n=2000001,r=1000000; int bit[n+3],A[100005],B[100005],del[100005]={0}; char op,buf[5000000],*p=buf,*o=buf; inline void upd(int x,int d){ for(;x<=n;x+=x&-x) bit[x]+=d;} inline int qry(int x){ int s=0; for(;x;x-=x&-x) s+=bit[x]; return s;} inline int getint(){ int s=0,f=1; while((*p!='-')&&(*p<'0'||*p>'9'))p++; if(*p=='-')f=-1,p++; while(*p>='0'&&*p<='9')s=s*10+*p++-48; return s*f; }inline char getop(){ while(*p!='Q'&&*p!='A'&&*p!='D')p++; return *p++; }inline void print(int x){ char str[30],*pt=str; if(!x)*o++=48; else{ while(x) *pt++=x%10+48,x/=10; while(pt--!=str)*o++=*pt;} }int main(){ fread(p,1,5000000,stdin); int q,a,b,k,i=0,t1,t2,An; q=getint(); for(int qq=1;qq<=q;qq++){ op=getop(); if(op=='A'){ a=getint(),t1=getint(),t2=getint(); b=t2-t1; ++i; A[i]=a; B[i]=b; if(a==0){ if(b<0)upd(1,1);} else if(b==0){ if(a<0) upd(1,1),upd(d,-1); else upd(1+d,1); }else if(b>0){ if(a>0){ An=b/a+1; if(-r<=An&&An<=r) upd(An+d,1); else if(An<-r) upd(1,1); }else{ An=b/(-a)+1; if(-r<=-An&&-An<=r) upd(1,1),upd(-An+d+1,-1); else if(-An>r) upd(1,1); } }else if(b<0){ if(a>0){ An=(-b-1)/a; if(-r<=-An&&-An<=r) upd(-An+d,1); else if(-An<-r) upd(1,1); }else{ An=(-b-1)/(-a); if(-r<=An&&An<=r) upd(1,1),upd(An+d+1,-1); else if(An>r) upd(1,1); } } }else if(op=='Q'){ k=getint(); print(qry(k+d));*o++='\n'; }else{ k=getint(); if(del[k])continue; del[k]=1; a=A[k]; b=B[k]; if(a==0){ if(b<0)upd(1,-1);} else if(b==0){ if(a<0) upd(1,-1),upd(d,1); else upd(1+d,-1); }else if(b>0){ if(a>0){ An=b/a+1; if(-r<=An&&An<=r) upd(An+d,-1); else if(An<-r) upd(1,-1); }else{ An=b/(-a)+1; if(-r<=-An&&-An<=r) upd(1,-1),upd(-An+d+1,1); else if(-An>r) upd(1,-1); } }else if(b<0){ if(a>0){ An=(-b-1)/a; if(-r<=-An&&-An<=r) upd(-An+d,-1); else if(-An<-r) upd(1,-1); }else{ An=(-b-1)/(-a); if(-r<=An&&An<=r) upd(1,-1),upd(An+d+1,1); else if(An>r) upd(1,-1); } } } }return fwrite(buf,1,o-buf,stdout),0; }
以及这是我贡献的一片红色。