(三道題題目名$OJ$上抄錯了兩個 )
大約是比較正常的一場。
基本沒幾檔暴力分,沒有亂搞,沒有構造,三道題都差不多有人講過($T1$聯賽模擬的老套路$T2LNC$的數學專題選講$T3cqbz$大神的$dp$選講)
遇到原題/講過的題就崩這是鐵律
但是得虧沒去寫$T3$不然估計得當場暴斃。。。
三道題打得都是大暴力。$T2$會$50$但是懶得爲了$20$分寫個多項式再調半天了。
垃圾考試,垃圾的我。
T1:獻給逝去公主的七重奏
大意:樹,每次操作會使所有節點的權值都變爲子樹權值的異或和,多次詢問$t$次操作後根節點的權值。$n,q \le 2 \times 10^5,t \le 10^9$
考慮每個點對根的貢獻,與深度有關,是個組合數。所以同一深度的所有點之間可以合併起來
然後我們要求的就是這個組合數是奇數的點的權值和。
用一下聯賽模擬42的結論,我們要求的是所有與$t-1$按位與之後爲$0$的$x$的權值和。$FWT$即可。$O(nlogn)$
1 #include<cstdio> 2 #define S 400005 3 int fir[S],l[S],to[S],ec,n,q,maxd,tot[S],w[S]; 4 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 5 void dfs(int p,int d,int fa=0){ 6 tot[d]^=w[p]; if(d>maxd)maxd=d; 7 for(int i=fir[p];i;i=l[i])if(to[i]!=fa)dfs(to[i],d+1,p); 8 } 9 int main(){ 10 scanf("%d%d",&n,&q); 11 for(int i=1,a,b;i<n;++i)scanf("%d%d",&a,&b),link(a,b),link(b,a); 12 for(int i=1;i<=n;++i)scanf("%d",&w[i]); 13 dfs(1,0); int l=1; while(l<=maxd)l<<=1; 14 for(int i=1;i<l;i<<=1)for(int j=0;j<l;j+=i<<1)for(int k=j;k<j+i;++k)tot[k+i]^=tot[k]; 15 for(int i=1,t;i<=q;++i)scanf("%d",&t),printf("%d\n",t?tot[l-1^(t-1&l-1)]:w[1]); 16 }
T2:幽雅地綻放吧,墨染的櫻花
大意:每個點有點權$w_i$。滿足$i,j$之間有$w_i \times w_j$條邊,一棵樹的權值定義爲所有節點的度數之積。求所有生成樹權值和。$n \le 10^5$
度數。應該先想到$prufer$。
每種$prufer$都對應一棵樹,每個數字在$prufer$中出現次數就是度數$-1$
同一個形態的樹的出現次數是邊權的和,也就是$\prod w_i^{deg_i}$。
枚舉每個點在$prufer$中的出現次數$a_i$。也就是$deg_i -1$
總的貢獻就是$(\prod\limits_{i=1}^{n} w_i ) (n-2)! \times \sum\limits_{a} [\sum\limits_{i=1}^{n} a_i =n-2] \prod\limits_{i=1}^{n} \frac{(a_i+1)w_i^{a_i}}{a_i!}$
上面那個$\prod (a_i+1)$形式很好,也就是說枚舉所有$[1,n]$的子集,可選可不選,也就是子集積的和。設$b$爲其子集元素。
$(\prod\limits_{i=1}^{n} w_i ) (n-2)! \times \sum\limits_{a} [\sum\limits_{i=1}^{n} a_i =n-2] \prod\limits_{i=1}^{n} \frac{w_i^{a_i}}{a_i!} \sum\limits_{b \subseteq [1,n]} \prod\limits_{i=1}^{n} a_{b_i}$
改變枚舉順序。同時我們把所有在$b$中的元素$x$的$a_x$值減少$1$。
考慮產生的影響,首先$w_i^{a_i}$那裏會少算一個$w_i$,提出來一起算,然後分母上的$a_i!$少乘一項也就是整個式子乘了$a_i$,發現後面也有個$\prod a_{b_i}}$恰好抵消了,就不用管了
$(\prod\limits_{i=1}^{n} w_i ) (n-2)! \times \sum\limits_{b \subseteq [1,n]} \prod\limits_{i=1}^{n} w_{b_i} \sum\limits_{a} [\sum\limits_{i=1}^{n} a_i =n-2-|b|] \prod\limits_{i=1}^{n} \frac{w_i^{a_i}}{a_i!} $
考慮最後$a$那個部分。如果把式子乘上一個$(n-2-|b|)!$,會結合分母上的階乘形成一個多重集排列。
考慮實際含義,就是把各種$w_i$填在一個長爲$n-2-|b|$的序列上,一個序列的權值是所有元素積,求所有序列的權值和。
所以再把$(n-2-|b|)!$除回去的話我們就能得到我們要求的式子是:
$(\prod\limits_{i=1}^{n} w_i ) (n-2)! \times \sum\limits_{b \subseteq [1,n]} \prod\limits_{i=1}^{n} w_{b_i} \frac{(\sum w_i}^{n-2-|b|}{(n-2-|b|)!} $
我們發現現在後面那一坨只與$|b|$有關而與其中具體有哪些元素無關。我們只在意大小。式子的中部是說同一種大小的子集的積的和。
對於每個$i$構造生成函數$F_i(x)=1+w_ix$。把$n$項都乘起來就知道大小爲某特定值的權值和。分治$NTT$即可。
1 #include<cstdio> 2 const int S=1<<22,mod=998244353; 3 char IO[S],*p=IO; 4 void rin(int&x){x=0;while(*p<48||*p>57)p++; while(*p>47&&*p<58)x=x*10+*p++-48;} 5 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 6 int mo(int x){return x>=mod?x-mod:x;} 7 int fac[S],inv[S],w[S],n,ans,pw=1,sw,pool[S],L[S],R[S],P,len,rev[S]; 8 #define lc p<<1 9 #define rc lc|1 10 #define md (l+r>>1) 11 void NTT(int*a,int op=1){ 12 for(int i=0,x;i<len;++i)if(rev[i]>i)x=a[i],a[i]=a[rev[i]],a[rev[i]]=x; 13 for(int i=1;i<len;i<<=1)for(int j=0,t=qp(3,(mod-1)/i/2*op+mod-1);j<len;j+=i<<1) 14 for(int k=j,w=1,x,y;k<j+i;++k,w=1ll*w*t%mod) 15 x=a[k],y=1ll*w*a[k+i]%mod,a[k]=mo(x+y),a[k+i]=mo(mod+x-y); 16 if(op==-1)for(int i=0,iv=qp(len,mod-2);i<len;++i)a[i]=1ll*a[i]*iv%mod; 17 } 18 void sat(int x){ 19 len=1;while(len<x)len<<=1; 20 for(int i=0;i<len;++i)rev[i]=rev[i>>1]>>1|(i&1?len>>1:0); 21 } 22 void build(int p,int l,int r){ 23 L[p]=P; R[p]=P+=r-l+1; P++; 24 if(l==r){pool[L[p]]=1;pool[R[p]]=w[l];return;} 25 build(lc,l,md);build(rc,md+1,r); 26 static int A[S],B[S]; sat(r-l+2); 27 for(int i=L[lc];i<=R[lc];++i)A[i-L[lc]]=pool[i]; 28 for(int i=L[rc];i<=R[rc];++i)B[i-L[rc]]=pool[i]; 29 NTT(A);NTT(B);for(int i=0;i<len;++i)A[i]=1ll*A[i]*B[i]%mod;NTT(A,-1); 30 for(int i=L[p];i<=R[p];++i)pool[i]=A[i-L[p]]; 31 for(int i=0;i<len;++i)A[i]=B[i]=0; 32 } 33 int main(){ 34 fread(IO,1,S,stdin); rin(n); 35 for(int i=1;i<=n;++i)rin(w[i]),pw=1ll*pw*w[i]%mod,sw=mo(sw+w[i]); 36 for(int i=fac[0]=1;i<=n;++i)fac[i]=fac[i-1]*1ll*i%mod; 37 inv[n]=qp(fac[n],mod-2); 38 for(int i=n-1;~i;--i)inv[i]=inv[i+1]*(i+1ll)%mod; 39 build(1,1,n); 40 for(int k=n-2,x=1;~k;--k)ans=(ans+1ll*inv[n-2-k]*pool[k]%mod*x)%mod,x=1ll*x*sw%mod; 41 printf("%lld\n",1ll*fac[n-2]*pw%mod*ans%mod); 42 }
T3:竹取飛翔
大意:樹,支持插入或刪除帶權路徑,每次操作後詢問 求一條路徑使得與這條路徑相交的路徑的權值和儘量大。$n,m \le 10^5$
專題分享的原題。
相交的路徑滿足其一的$LCA$在另一條路徑上。($LCA$相同的情況歸類到其中一種)
我們可以維護$A$表示經過該點的路徑且$LCA$不是該點的路徑權值和,維護$B$表示以該點爲$LCA$的路徑的權值和。
我們要查詢的是每個點 $A$值加上從這個點開始的兩條不同下行鏈的最大$B$值 的最大值。
我們的操作就是要支持 鏈加$A$,單點加$B$,維護每個點的最大/次大下行鏈$B$和,維護每個點作爲$LCA$時產生的答案。
後兩個東西可以直接用一個可刪堆解決。
進行樹鏈剖分,那麼答案怎麼表示?
只考慮路徑$LCA$所在的重鏈,設之爲$x$,然後最優路徑在某個點$y$處脫離了重鏈。
這樣對答案的貢獻就是:$A[x]+totB]x][y]+downB[x]+downB[y]$。其中$downB$特指沿輕鏈下行最大權值。
所以如果我們維護的好$downB,A$
注意特殊處理$x=y$的情況,其中一個$downB$是次大的。
考慮每次修改的時候我們都幹了點啥:
修改$x-LCA,y-LCA$的$A$值,直接區間加。樹剖線段樹沒啥問題。
注意這裏的區間加不是單純的每個點都加,而是點加邊減,這樣才能保證每條路徑被計數一遍。
維護$tag$表示區間加懶標記,$ex$表示左右兒子之間的邊權和。區間查詢如果跨越中點的話需要減去$ex$
如果能維護出這個東西,我們就能發現其實我們需要的就是一個最大區間和了。
然後跳祖先更新$B$。每次是重鏈上的前綴加和單點修改($x=y$的情況)
實現細節的話,可以選擇對於每條重鏈開一棵樹,常數小,不需要區間查詢(直接訪問跟節點維護的最優值)
跳重鏈更新$B$的時候可以剪枝,就是如果和原值一樣就不改,查詢的方式也可以直接訪問跟節點所維護的前綴最優值(維護最大子段和的副產品)減去這個點的$A$值
時間複雜度$O(nlogn)$。代碼複雜度螺旋昇天。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 400005 4 #define ll long long 5 struct Heap{ 6 priority_queue<ll>p,r; 7 void push(ll x){p.push(x);} 8 void pop(ll x){r.push(x);} 9 ll top(){while(r.size()&&p.top()==r.top())p.pop(),r.pop();return p.top();} 10 ll sec(){ll x=top(),y;pop(x);y=top();push(x);return y;} 11 }ans,s[S>>2]; 12 int n,m,a[S],b[S],w[S],l[S],fir[S],to[S],ec,len[S]; char o[5]; 13 int top[S],dfn[S],tim,f[S],sz[S],son[S],dep[S]; 14 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 15 void dfs(int p,int fa){ 16 sz[p]=1; dep[p]=dep[fa]+1; f[p]=fa; 17 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 18 dfs(to[i],p); sz[p]+=sz[to[i]]; 19 if(sz[to[i]]>sz[son[p]])son[p]=to[i]; 20 } 21 } 22 void DFS(int p,int tp){ 23 dfn[p]=++tim; top[p]=tp; 24 if(son[p])DFS(son[p],tp); else ans.push(0),len[tp]=dfn[p]-dfn[tp]; 25 for(int i=fir[p];i;i=l[i])if(!dfn[to[i]])DFS(to[i],to[i]),s[p].push(0); 26 } 27 ll tot[S],mx[S],lmx[S],rmx[S],lz[S],ex[S],exA[S];int rt[S],pc,Lc[S],Rc[S]; 28 #define lc Lc[p] 29 #define rc Rc[p] 30 #define md (L+R>>1) 31 void up(int p){ 32 lmx[p]=max(lmx[lc],tot[lc]+lmx[rc]-ex[p])+lz[p]; 33 rmx[p]=max(rmx[rc],tot[rc]+rmx[lc]-ex[p])+lz[p]; 34 mx[p]=max(max(mx[lc],mx[rc]),lmx[rc]+rmx[lc]-ex[p])+lz[p]; 35 tot[p]=tot[lc]+tot[rc]-ex[p]+lz[p]; 36 } 37 void add(int l,int r,int w,int&p,int L,int R){ 38 if(!p)p=++pc; 39 if(l<=L&&R<=r){lmx[p]+=w;rmx[p]+=w;mx[p]+=w;tot[p]+=w;lz[p]+=w;return;} 40 if(r<=md)add(l,r,w,lc,L,md); else if(l>md)add(l,r,w,rc,md+1,R); 41 else ex[p]+=w,add(l,r,w,lc,L,md),add(l,r,w,rc,md+1,R); up(p); 42 } 43 void chg(int P,ll v1,ll v2,int&p,int L,int R){ 44 if(!p)p=++pc; 45 if(L==R){lmx[p]=rmx[p]=mx[p]=v1+lz[p];mx[p]+=v2;return;} 46 if(P>md)chg(P,v1,v2,rc,md+1,R); else chg(P,v1,v2,lc,L,md); up(p); 47 } 48 void upd(int p){static ll B[S];if(B[p]!=mx[rt[p]])ans.pop(B[p]),ans.push(B[p]=mx[rt[p]]);} 49 bool updB(int p){ 50 static ll B[S]; int F=f[p],T=top[F]; 51 if(lmx[rt[p]]-exA[p]==B[p])return 0; 52 s[F].pop(B[p]); s[F].push(B[p]=lmx[rt[p]]-exA[p]); 53 return chg(dfn[F],s[F].top(),s[F].sec(),rt[T],dfn[T],dfn[T]+len[T]),1; 54 } 55 void upd(int x,int y,int w){int T; 56 while(top[x]!=top[y]){ 57 if(dep[top[x]]<dep[top[y]])swap(x,y); T=top[x]; 58 add(dfn[T],dfn[x],w,rt[T],dfn[T],dfn[T]+len[T]); 59 x=T; exA[x]+=w; updB(x); upd(x); x=f[x]; 60 } 61 if(dfn[x]>dfn[y])swap(x,y); T=top[x]; add(dfn[x],dfn[y],w,rt[T],dfn[T],dfn[T]+len[T]); 62 for(upd(x=T);f[x]&&updB(x);upd(x=top[f[x]])); 63 } 64 int main(){ 65 scanf("%d%d",&n,&m); 66 for(int i=1,x,y;i<n;++i)scanf("%d%d",&x,&y),link(x,y),link(y,x); 67 dfs(1,0); DFS(1,1); 68 for(int i=1,x;i<=m;++i){ 69 scanf("%s",o); 70 if(o[0]=='+')scanf("%d%d%d",&a[i],&b[i],&w[i]),upd(a[i],b[i],w[i]); 71 else scanf("%d",&x),upd(a[x],b[x],-w[x]); 72 printf("%lld\n",ans.top()); 73 } 74 }