第一次打字竞赛QwQ
T1
内存限制:512 MiB
时间限制:1000 ms
由于 zzq 太懒了,所以这题没有题目背景。
有一棵树,树上有 $n$ 个点,每条边上有一个非负边权。
在这 $n$ 个点中有 $k$ 个特殊点,其中 $k$ 为偶数。定义两个点的距离为它们在树上的简单路径上的边权之和。你需要将这 $k$ 个点配成 $\frac{k}{2}$ 个互不相交的对,并最大化每一对点的距离之和。
$1 \leq n,c \leq 10^5$
题解
最终所有的路径会交于一点,如果没有交于一点的话,交于一点更优
所以只要一个点,如果以这个点为根,最大的子树内含有的配对点数 $\leq \frac{k}{2}$ ,则以这个点为根去配对即可
#include <bits/stdc++.h> using namespace std; const int N=1e5+5; int n,k,hd[N],nx[N*2],t,V[N*2],sz[N],ax[N],now; bool vis[N];stack<int>g[N]; struct O{ int x,s; friend bool operator < (const O& A,const O& B){ return A.s<B.s; } }; priority_queue<O>q; void add(int u,int v){ V[++t]=v;nx[t]=hd[u];hd[u]=t; } void get(int x,int fa){ if (vis[x]) g[now].push(x); for (int i=hd[x];i;i=nx[i]) if (V[i]!=fa) get(V[i],x); } void dfs(int x,int fa){ sz[x]=vis[x]; for (int i=hd[x];i;i=nx[i]) if (V[i]!=fa) dfs(V[i],x),sz[x]+=sz[V[i]], ax[x]=max(ax[x],sz[V[i]]); ax[x]=max(ax[x],k-sz[x]); if ((ax[x]<<1)<=k){ for (int i=hd[x];i;i=nx[i]){ now=V[i],get(V[i],x); int s=g[V[i]].size(); if (s) q.push(O{V[i],s}); } if (vis[x]) g[x].push(x),q.push((O){x,1}); while(!q.empty()){ int A=q.top().x;q.pop(); int B=q.top().x;q.pop(); printf("%d %d\n",g[A].top(),g[B].top()); g[A].pop();g[B].pop(); int sA=g[A].size(),sB=g[B].size(); if (sA) q.push((O){A,sA}); if (sB) q.push((O){B,sB}); } exit(0); } } int main(){ scanf("%d%d",&n,&k); for (int x,y,z,i=1;i<n;i++) scanf("%d%d%d",&x,&y,&z), add(x,y),add(y,x); for (int x,i=1;i<=k;i++) scanf("%d",&x),vis[x]=1; dfs(1,0);return 0; }
T2
内存限制:256 MiB
时间限制:1000 ms
给定字符集大小 $S$ ,问有多少个长度为 $N$ 的字符串不存在长度 $>1$ 的回文后缀。
答案对 $M$ 取模。
$1 \leq n \leq 10^7,1\leq S < M \leq 2^{30}-1$
题解
将字符串倒着看,设 $f_i$ 表示前 $i$ 位且满足条件的字符串的方案数
则 $f_i=f_{i-1} \times s - f_{\frac{i+1}{2}}$
(前 $\frac{i+1}{2}$ 不为回文,将其反过来为回文
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=1e7+5; LL s,m,f[N];int n; int main(){ scanf("%d%lld%lld",&n,&s,&m); f[0]=1;for (int i=1;i<=n;i++) f[i]=(f[i-1]*s-f[(i+1)/2])%m; return printf("%lld\n",f[n]),0; }
T3
内存限制:512 MiB
时间限制:1000 ms
有一棵 $n$ 个点的树,每个节点上有一个权值 $w_i$ ,最开始根为 $1$ 号点。现在有 $3$ 种类型的操作:
· 1 $root$ , 表示将根设为 $root$ 。
· 2 $u$ $v$ $x$ , 设 $u,v$ 的最近公共祖先为 $p$ , 将 $p$ 的子树中的所有点的权值加上 $x$ 。
· 3 $u$ ,查询 $u$ 的子树中的所有点的权值和。
对于每个 3
操作,输出答案。
$1 \leq n,q \leq 3 \times 10^5,-10^7 \leq w_i,x \leq 10^7$
题解
没有真的换根呦
对于 2
操作,我们可以进行一下分讨
设 $l=lca(u,v)$
- $u$ 在 $root$ 子树内, $v$ 在 $root$ 子树内
如果 $l=rt$ 就是全部 $+x$ ,否则就是 $l$ 子树 $+x$ - $u$ 在 $root$ 子树内, $v$ 不在 $root$ 子树内/ $u$ 不在 $root$ 子树内, $v$ 在 $root$ 子树内
全部 $+x$ - $u$ 不在 $root$ 子树内, $v$ 不在 $root$ 子树内
如果 $l$ 不为 $root$ 的祖先,就是 $l$ 子树 $+x$
否则先全部 $+x$ ,然后找到 $root$ 到 $l$ 的路径上最后一个不在 $u$ 到 $v$ 这条路径上的点 $w$ , $w$ 子树 $+x$
对于3
操作进行类似的分讨,即可过题
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=3e5+5; int rt,q,w[N],n,c,f[N*2][22],hd[N],nx[N*2],V[N*2],t,tt,id[N],dfn[N],e[N],d[N],ot[N],lg[N*2],fa[N][22]; LL s[N*4],ans,tg[N*4]; void add(int u,int v){ V[++t]=v;nx[t]=hd[u];hd[u]=t; } void dfs(int x,int F){ f[++c][0]=x;d[x]=d[F]+1;fa[x][0]=F; for (int i=1;fa[fa[x][i-1]][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1]; dfn[id[x]=++tt]=x;e[x]=c; for (int i=hd[x];i;i=nx[i]) if (V[i]!=F) dfs(V[i],x),f[++c][0]=x; ot[x]=tt; } int lca(int l,int r){ if (l>r) swap(l,r);int i=log2(r-l+1); if (d[f[l][i]]<d[f[r-(1<<i)+1][i]]) return f[l][i]; return f[r-(1<<i)+1][i]; } bool pd(int x,int y){ return (id[x]>=id[y] && id[x]<=ot[y]); } #define Ls k<<1 #define Rs k<<1|1 #define mid ((l+r)>>1) void pushdown(int k,int l,int r){ LL v=tg[k];tg[k]=0; s[Ls]+=v*(mid-l+1);tg[Ls]+=v; s[Rs]+=v*(r-mid);tg[Rs]+=v; } void build(int k,int l,int r){ if (l==r) s[k]=w[dfn[l]]; else build(Ls,l,mid), build(Rs,mid+1,r), s[k]=s[Ls]+s[Rs]; } void update(int k,int l,int r,int L,int R,int v){ if (L<=l && r<=R){ s[k]+=1ll*v*(r-l+1);tg[k]+=v;return; } if (tg[k]) pushdown(k,l,r); if (mid>=L) update(Ls,l,mid,L,R,v); if (mid<R) update(Rs,mid+1,r,L,R,v); s[k]=s[Ls]+s[Rs]; } LL query(int k,int l,int r,int L,int R){ if (L<=l && r<=R) return s[k]; if (tg[k]) pushdown(k,l,r); if (mid<L) return query(Rs,mid+1,r,L,R); else if (mid>=R) return query(Ls,l,mid,L,R); return query(Rs,mid+1,r,L,R)+query(Ls,l,mid,L,R); } int main(){ for (int i=0;i<20;i++) lg[1<<i]=i; scanf("%d%d",&n,&q);rt=1; for (int i=1;i<=n;i++) scanf("%d",&w[i]); for (int x,y,i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1,0);build(1,1,n); for (int i=c;i;i--) for (int j=1;i+(1<<j)<=c+1;j++) if (d[f[i][j-1]]<d[f[i+(1<<(j-1))][j-1]]) f[i][j]=f[i][j-1]; else f[i][j]=f[i+(1<<(j-1))][j-1]; for (int op,u,v,l,x;q--;){ scanf("%d",&op); if (op<2) scanf("%d",&rt); else if (op>2){ scanf("%d",&l); if (pd(l,rt)){ if (l==rt) ans=query(1,1,n,1,n); else ans=query(1,1,n,id[l],ot[l]); } else{ if (lca(e[l],e[rt])==l){ ans=query(1,1,n,1,n); l=d[rt]-d[l]-1;v=rt; for (;l;l-=l&-l) v=fa[v][lg[l&-l]]; ans-=query(1,1,n,id[v],ot[v]); } else ans=query(1,1,n,id[l],ot[l]); } printf("%lld\n",ans); } else{ scanf("%d%d%d",&u,&v,&x); if (pd(u,rt)){ if (pd(v,rt)){ l=lca(e[u],e[v]); if (l==rt) update(1,1,n,1,n,x); else update(1,1,n,id[l],ot[l],x); } else update(1,1,n,1,n,x); } else{ if (pd(v,rt)) update(1,1,n,1,n,x); else{ l=lca(e[u],e[v]); if (pd(rt,l)){ update(1,1,n,1,n,x);l=rt; for (int i=19;~i;i--) if (fa[l][i] && !pd(u,fa[l][i]) && !pd(v,fa[l][i])) l=fa[l][i]; update(1,1,n,id[l],ot[l],-x); } else update(1,1,n,id[l],ot[l],x); } } } } return 0; }