2020 江蘇省賽A Array 線段樹的區間對映 or 線段樹合併
阿新 • • 發佈:2020-12-27
這題還是很有意思的
我們觀察題目發現 3操作很難維護(對於線段樹維護冪次方和 一般只能很低的冪 比如二次方 三次方 這也很麻煩了)
但是我們發現 p特別小 只有30
於是我們可以維護一個對映
最常用的方法就是 開30棵線段樹 然後用懶惰標記維護一個對映
例如 1再經過某個操作之後變成了3 那麼tag[id][1]就是3
注意在下推標記的時候 要考慮標記的迭代
這是第一種做法 耗時2636ms
#include<bits/stdc++.h> using namespace std; const int N = 4e5+100; int sum[32][N<<2],lazy[32][N<<2]; int phi,p,gn; int num[32],ct[32],val[N]; int eular(int n){ int ans=n; for(int i = 2; i*i <= n; i++){ if(n%i==0){ ans=ans/i*(i-1); while(n%i==0) n/=i; } } if(n>1) ans=ans/n*(n-1); return ans; } int e_qpow(int a,int b){ if(__gcd(a,p)==1){ b = b%phi; }else if(b >= phi){ b = b%phi+phi; } int ans=1; while(b){ if(b&1) ans=(ans*a)%p; a=(a*a)%p; b>>=1; } return ans; } #define ls id<<1 #define rs ls|1 #define lson ls,l,mid #define rson rs,mid+1,r #define mid (l+r>>1) void pushup(int id){ for(int i = 0; i < p; i++) sum[i][id]=(sum[i][ls]+sum[i][rs]); } void pushdown(int id){ for(int i = 0; i < p; i++){ num[i]=sum[i][ls]; sum[i][ls]=0; } for(int i = 0; i < p; i++){ sum[lazy[i][id]][ls]+=num[i]; } for(int i = 0; i < p; i++){ num[i]=sum[i][rs]; sum[i][rs]=0; } for(int i = 0; i < p; i++){ sum[lazy[i][id]][rs]+=num[i]; } for(int i = 0; i < p; i++){ lazy[i][ls]=lazy[lazy[i][ls]][id]; lazy[i][rs]=lazy[lazy[i][rs]][id]; } for(int i = 0; i < p; i++) lazy[i][id]=i; } void build(int id,int l,int r){ if(l==r){ scanf("%d",&val[l]); sum[val[l]%p][id]=1; return; } for(int i = 0; i < p; i++) lazy[i][id]=i; build(lson);build(rson); pushup(id); } int get(int op,int x,int v){ if(op==1) return (x+v)%p; else if(op==2) return 1ll*x*v%p; else return e_qpow(x,v)%p; } void update(int id,int l,int r,int L,int R,int op,int v){ if(L<=l&&R>=r){ for(int i = 0; i < p; i++){ num[i]=sum[i][id]; sum[i][id]=0; } for(int i = 0; i < p; i++){ int x = get(op,i,v); sum[x][id]+=num[i]; lazy[i][id]=get(op,lazy[i][id],v); } return; } pushdown(id); if(L<=mid) update(lson,L,R,op,v); if(R>mid) update(rson,L,R,op,v); pushup(id); } void query(int id,int l,int r,int L,int R){ if(L<=l&&R>=r){ for(int i = 0; i < p; i++) ct[i]+=sum[i][id]; return; } pushdown(id); if(L<=mid) query(lson,L,R); if(R>mid) query(rson,L,R); } int main(){ //for(int i = 2; i <= 30; i++) printf("i=%d eular=%d\n",i,eular(i)); scanf("%d%d",&gn,&p); phi=eular(p); build(1,1,gn); int q; scanf("%d",&q); while(q--){ int op,l,r,k; scanf("%d%d%d%d",&op,&l,&r,&k); if(op<=3){ update(1,1,gn,l,r,op,k); }else if(op==4){ memset(ct,0,sizeof(ct)); query(1,1,gn,l,r); int ans = 0; for(int i = 0; i < p; i++){ ans=(ans+e_qpow(i,k)*ct[i])%p; } //printf("%d\n",ans); }else if(op==5){ memset(ct,0,sizeof(ct)); query(1,1,gn,l,r); int ans = 1; for(int i = 0; i < p; i++){ ans=(ans*e_qpow(i,ct[i]))%p; } // printf("%d\n",ans); } memset(ct,0,sizeof(ct)); query(1,1,gn,1,gn); for(int i = 0; i < p; i++){ printf("i=%d sum=%d\n",i,ct[i]); } } return 0; }
第二種做法 是由CF991G Mass Change Queries 啟發我的
這題題意很簡單 就是把區間裡面為x的數轉換為y 我尋思這不就是一個對映嗎
然後發現這題有兩種題解 其一是上述那種方法 跑的很慢 其二就是線段樹合併了 這東西是真的強 而且跑的很快
線段樹合併和fhq平衡樹很相似(具體我也不知道啥時候用線段樹合併 啥時候用fhq fhq感覺處理前後驅問題更好)
此處用線段樹合併可以很快的完成對映操作
對於每一個值 我們維護一棵線段樹
然後1,2,3號操作都是對映操作 我們求出每個值的對映
把對應的線段樹分裂開來 最後再合併在一起
速度很快 只跑了 748ms 在cf上rk1
#include<bits/stdc++.h> using namespace std; const int N = 4e5+100; int phi,p,gn,q; struct node{ int l,r,v; }t[N*50]; int rt[40],nrt[40],mp[40]; int plot[N*50],cnt,tot; int eular(int n){ int ans=n; for(int i = 2; i*i <= n; i++){ if(n%i==0){ ans=ans/i*(i-1); while(n%i==0) n/=i; } } if(n>1) ans=ans/n*(n-1); return ans; } int e_qpow(int a,int b){ if(__gcd(a,p)==1){ b = b%phi; }else if(b >= phi){ b = b%phi+phi; } int ans=1; while(b){ if(b&1) ans=(ans*a)%p; a=(a*a)%p; b>>=1; } return ans; } inline int newnode(){ return cnt?plot[cnt--]:++tot; } void del(int x){ t[x].l=t[x].r=t[x].v=0; plot[++cnt]=x; } int merge(int x,int y){ if(!x||!y) return x+y; t[x].v+=t[y].v; t[x].l=merge(t[x].l,t[y].l); t[x].r=merge(t[x].r,t[y].r); del(y); return x; } void pushup(int o){ t[o].v=t[t[o].l].v+t[t[o].r].v; } void update(int &o,int l,int r,int pos){ if(!o) o=newnode(); t[o].v++; if(l==r) return; int mid = l+r>>1; if(pos<=mid) update(t[o].l,l,mid,pos); else update(t[o].r,mid+1,r,pos); } void change(int &x,int &y,int l,int r,int L,int R){ if(!x) return; if(L<=l&&R>=r){ y=merge(x,y);x=0; return; } if(!y) y=newnode(); int mid = l+r>>1; if(L<=mid) change(t[x].l,t[y].l,l,mid,L,R); if(R>mid) change(t[x].r,t[y].r,mid+1,r,L,R); pushup(x); pushup(y); } void getinfo(int T,int l,int r,int k){ for(int i = 0; i < p; i++){ if(T==1) mp[i]=(i+k)%p; else if(T==2) mp[i]=(1ll*i*k)%p; else mp[i]=e_qpow(i,k)%p; nrt[i]=0; } for(int i = 0; i < p; i++){ if(i==mp[i]) continue; change(rt[i],nrt[mp[i]],1,gn,l,r); } for(int i = 0; i < p; i++){ rt[i]=merge(rt[i],nrt[i]); } } int query(int o,int l,int r,int L,int R){ if(!o) return 0; if(L<=l&&R>=r) return t[o].v; int mid = l+r>>1,ret = 0; if(L<=mid) ret+=query(t[o].l,l,mid,L,R); if(R>mid) ret+=query(t[o].r,mid+1,r,L,R); return ret; } int main(){ scanf("%d%d",&gn,&p); phi=eular(p); for(int i = 1; i <= gn; i++){ int v; scanf("%d",&v); update(rt[v%p],1,gn,i); } scanf("%d",&q); for(int i = 1; i <= q; i++){ int T,l,r,k; scanf("%d%d%d%d",&T,&l,&r,&k); if(T<=3){ getinfo(T,l,r,k); }else if(T==4){ int ans=0; for(int i = 0; i < p; i++){ ans=(ans+e_qpow(i,k)*query(rt[i],1,gn,l,r)%p)%p; } printf("%d\n",ans); }else{ int ans=1; for(int i = 0; i < p; i++){ ans=(ans*e_qpow(i,query(rt[i],1,gn,l,r)))%p; } printf("%d\n",ans); } } return 0; }