[考试反思]0513省选模拟95:见识

(三道题题目名$OJ$上抄错了两个 )

大约是比较正常的一场。

基本没几档暴力分,没有乱搞,没有构造,三道题都差不多有人讲过($T1$联赛模拟的老套路$T2LNC$的数学专题选讲$T3cqbz$大神的$dp$选讲)

遇到原题/讲过的题就崩这是铁律

但是得亏没去写$T3$不然估计得当场暴毙。。。

三道题打得都是大暴力。$T2$会$50$但是懒得为了$20$分写个多项式再调半天了。

垃圾考试,垃圾的我。

T1:献给逝去公主的七重奏

大意:树,每次操作会使所有节点的权值都变为子树权值的异或和,多次询问$t$次操作后根节点的权值。$n,q le 2 imes 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
",t?tot[l-1^(t-1&l-1)]:w[1]);
16 }
View Code

T2:幽雅地绽放吧,墨染的樱花

大意:每个点有点权$w_i$。满足$i,j$之间有$w_i imes w_j$条边,一棵树的权值定义为所有节点的度数之积。求所有生成树权值和。$n le 10^5$

度数。应该先想到$prufer$。

每种$prufer$都对应一棵树,每个数字在$prufer$中出现次数就是度数$-1$

同一个形态的树的出现次数是边权的积,也就是$prod w_i^{deg_i}$。

枚举每个点在$prufer$中的出现次数$a_i$。也就是$deg_i -1$

总的贡献就是$(prodlimits_{i=1}^{n} w_i ) (n-2)! imes sumlimits_{a} [sumlimits_{i=1}^{n} a_i =n-2]  prodlimits_{i=1}^{n} frac{(a_i+1)w_i^{a_i}}{a_i!}$

上面那个$prod (a_i+1)$形式很好,也就是说枚举所有$[1,n]$的子集,可选可不选,也就是子集积的和。设$b$为其子集元素。

$(prodlimits_{i=1}^{n} w_i ) (n-2)! imes sumlimits_{a} [sumlimits_{i=1}^{n} a_i =n-2]  prodlimits_{i=1}^{n} frac{w_i^{a_i}}{a_i!} sumlimits_{b subseteq [1,n]} prodlimits_{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}$恰好抵消了,就不用管了

$(prodlimits_{i=1}^{n} w_i ) (n-2)! imes sumlimits_{b subseteq [1,n]} prodlimits_{i=1}^{n} w_{b_i} sumlimits_{a} [sumlimits_{i=1}^{n} a_i =n-2-|b|]  prodlimits_{i=1}^{n} frac{w_i^{a_i}}{a_i!} $

考虑最后$a$那个部分。如果把式子乘上一个$(n-2-|b|)!$,会结合分母上的阶乘形成一个多重集排列。

考虑实际含义,就是把各种$w_i$填在一个长为$n-2-|b|$的序列上,一个序列的权值是所有元素积,求所有序列的权值和。

所以再把$(n-2-|b|)!$除回去的话我们就能得到我们要求的式子是:

$(prodlimits_{i=1}^{n} w_i ) (n-2)! imes sumlimits_{b subseteq [1,n]} prodlimits_{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
",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
",ans.top());
73     }
74 }
View Code


 

原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12886183.html