[考試反思]0513省選模擬95:見識

(三道題題目名$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 }
View Code

 

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 }
View Code

 

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 }
View Code

 


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章