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


     

  • 相关阅读:
    How to display errors using Page_Error event of Page Object?
    DataGrid的一个用法!
    Android中Paint字体属性的一些设置
    Androidb不使用OpenGL实现3D旋转效果
    dip、dp、sp、px和pt的区别是什么?
    Windows Phone 入门教程
    苹果开发准备工作
    Testing和Instrumentation
    为Android添加一个新语种
    Android屏幕密度(Density)和分辨率的关系 及转换
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12886183.html
Copyright © 2011-2022 走看看