「2018山東一輪集訓」 Tree
為什麽出題人這麽毒瘤啊??!!一個分塊還要帶log的題非要出成n<=2*1e5。。。。。。。
為了卡過最後兩個點我做了無數常數優化,包括但不限於:把所有線段樹改成 存差分的樹狀數組;把樹剖求LCA的極小的log優化成rmq O(1)求LCA;根據測試情況手動調整siz的大小;
但就是死也卡不過去,算了算了QWQ
(常規套路,先把1設成根建有根樹)
這個題的主要思路就是 對節點的下標分塊,設 f[i][j] 為 第i個塊內所有點到點j的距離和,然後看如何快速的動態維護這個玩意。。。。
發現改動一條邊權的時候,只有兩個點分別位於這條邊兩側的時候才會對它們之間的dis有影響。
我們設p為邊端點中更深的那個,那麽也就是一個在p子樹內,一個在子樹外的才有影響。。。。
於是我們對每個塊開一個vector記錄一下這個塊內的點的dfs序集合,排完序之後就可以之間O(log)的查詢某個塊在一棵子樹內/外的點數了。。。
因為f[][]的第一維比較小,所以我們可以暴力枚舉第一維,然後對第二維進行快速的修改。。。。。這時候發現第二維如果是存dfs序的話會更加方便(子樹內可以直接進行區間修改),所以就改成下標代表dfs序啦。。。
對於整塊整塊的一些點到某個點的距離,用上述方法就行啦。。。可以發現都是區間修改單點查詢,所以用差分的樹狀數組可以快(可能還不止)4倍常數哦。。。
查詢零散的點對(i,j)之間的距離的話更加簡單。。可以動態維護dis[i]表示i到根的距離(發現也是區間修改單點查詢,所以可以類似上述整塊的方法處理),答案就是dis[i]+dis[j]-2*dis[LCA(i,j)]。。。
可能說起來不是很多吧qwq?但是要寫一輩子啊QWQWQWQ。。。。
(我美好的下午就這麽沒了QWQ)
話說我把樹剖求LCA改成rmq之後反而更慢了QWQ,這是什麽鬼啊。。。。
/* inside : b * derta outside : a * derta all -> a * derta inside -> (b-a) * derta */ #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<vector> #include<iostream> #define ll long long using namespace std; #define pb push_back const int maxn=200003,N=205; inline int read(){ int x=0; char ch=getchar(); for(;!isdigit(ch);ch=getchar()); for(;isdigit(ch);ch=getchar()) x=x*10+ch-‘0‘; return x; } void W(ll x){ if(x>=10) W(x/10); putchar(x%10+‘0‘);} int n,m,T,dep[maxn],siz[maxn],cl[maxn]; int F[maxn],dc,dfn[maxn],dy[maxn],son[maxn]; int bl[maxn],num,val[maxn*2],uu,vv,ww; int hd[maxn],ne[maxn*2],to[maxn*2]; ll ans=0,f[N][maxn]; vector<int> id[N]; char s[10]; void add(const int &x,const int &y,const int &z){ to[++num]=y,ne[num]=hd[x],hd[x]=num,val[num]=z; } void update(const int &T,int x,const int &y){ for(;x<=n;x+=x&-x) f[T][x]+=(ll)y;} ll query(const int &T,int x){ ll an=0; for(;x;x-=x&-x) an+=(ll)f[T][x]; return an;} void Fdfs(int x,int fa){ F[x]=fa,siz[x]=1; for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa){ dep[to[i]]=dep[x]+1,Fdfs(to[i],x),siz[x]+=siz[to[i]]; if(!son[x]||siz[to[i]]>siz[son[x]]) son[x]=to[i]; } } void Sdfs(int x,int tp){ dfn[x]=++dc,dy[dc]=x,cl[x]=tp; if(!son[x]) return; Sdfs(son[x],tp); for(int i=hd[x];i;i=ne[i]) if(to[i]!=F[x]&&to[i]!=son[x]) Sdfs(to[i],to[i]); } inline int LCA(int a,int b){ while(cl[a]!=cl[b]){ if(dep[cl[a]]>dep[cl[b]]) a=F[cl[a]]; else b=F[cl[b]]; } return dep[a]>dep[b]?b:a; } inline int Get(int T,int x){ return upper_bound(id[T].begin(),id[T].end(),x)-id[T].begin(); } inline void Maintain(int o,int derta){ int p=to[o*2-1]; if(dep[p]<dep[to[o<<1]]) p=to[o<<1]; update(0,dfn[p],derta),update(0,dfn[p]+siz[p],-derta); for(int i=1,a,b;i<=200;i++) if(id[i].size()){ a=Get(i,dfn[p]+siz[p]-1)-Get(i,dfn[p]-1),b=id[i].size()-a; update(i,1,a*derta),update(i,dfn[p],(b-a)*derta),update(i,dfn[p]+siz[p],(a-b)*derta); } } inline ll calc(int qz,int p){ ll an=0; for(int i=1;i<bl[qz];i++) an+=query(i,dfn[p]); for(int i=qz;i;i--){ an+=query(0,dfn[i])+query(0,dfn[p])-2ll*query(0,dfn[LCA(p,i)]); if(bl[i]!=bl[i-1]) break; } return an; } inline void prework(){ Fdfs(1,0),Sdfs(1,1); for(int i=1;i<=n;i++){ bl[i]=(i-1)/1000+1; id[bl[i]].pb(dfn[i]); } for(int i=1;i<=200;i++) sort(id[i].begin(),id[i].end()); for(int i=1;i<n;i++) Maintain(i,val[i<<1]); } inline void solve(){ const int ha=n; while(m--){ scanf("%s",s); if(s[0]==‘m‘){ uu=read(),vv=read(); if(T) uu^=ans,vv^=ans; Maintain(uu,vv-val[uu<<1]),val[uu<<1]=vv; } else{ uu=read(),vv=read(),ww=read(); if(T) uu^=ans,vv^=ans,ww^=ans; ans=calc(vv,ww)-calc(uu-1,ww); W(ans),puts(""),ans%=ha; } } } int main(){ // freopen("tree.in","r",stdin); // freopen("tree.out","w",stdout); n=read(),m=read(),T=read(); for(int i=1;i<n;i++){ uu=read(),vv=read(),ww=read(); add(uu,vv,ww),add(vv,uu,ww); } prework(); solve(); return 0; }
「2018山東一輪集訓」 Tree