zoukankan      html  css  js  c++  java
  • [考试反思]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


     

  • 相关阅读:
    ASP.NET编程的十大技巧
    C#学习心得(转)
    POJ 1177 Picture (线段树)
    POJ 3067 Japan (树状数组)
    POJ 2828 Buy Tickets (线段树)
    POJ 1195 Mobile phones (二维树状数组)
    HDU 4235 Flowers (线段树)
    POJ 2886 Who Gets the Most Candies? (线段树)
    POJ 2418 Cows (树状数组)
    HDU 4339 Query (线段树)
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12886183.html
Copyright © 2011-2022 走看看