11.5 game

题单介绍

### 11.5 ### t1 给定一颗以 $1$ 为根的 $n$ 个节点的树,每个节点有两个点权 $w_x,v_x$,每次操作可以交换选择一个节点 $x$ 和其一个儿子 $y$,交换 $w_x,w_y$,然后 $w_x \gets w_x+c$,$w_y \gets w_y-c$,其中 $c$ 是给定常数。 $1 \leq n \leq 2 \times 10^5$。 任意操作,最小化 $\sum_{i=1}^{n}|w_i-v_i|$。 考虑将一个节点上移一步会 $+c$,将一个节点下移会 $-c$,所以预处理每个点的深度 $dep_i$,然后将 $w_x \gets w_x+dep_xc$,$v_x \gets v_x+dep_xc$,对 $w$ 和 $v$ 分别排序后贪心依次匹配即可。 ```cpp #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cctype> #define ll long long #define FOR(i,a,b) for(int i = (a);i <= (b);++i) #define REP(i,a,b) for(int i = (a);i >= (b);--i) #define GO(x) for(int i = h[x],y = e[i];i;y = e[i=ne[i]]) static char stkk[200]; template<typename T>inline void output(T x){ if(!x)return putchar('0'),void(); if(x<0)x = ~x+1,putchar('-'); int top = 0; for(;x;stkk[++top]=x%10^48,x/=10); for(;top;putchar(stkk[top--])); } template<typename T>inline void readx(T &x){ x = 0;int y = 1;char c = getchar(); for(;c<48||c>58;c = getchar())if(c=='-')y = -1; for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48); x *= y; } const int N = 2e5+10; static int e[N],ne[N],h[N],tot; inline void add(int x,int y){e[++tot] = y,ne[tot] = h[x],h[x] = tot;} static int dep[N],n; static ll w[N],v[N],c; void dfs(int x){ GO(x)dep[y] = dep[x]+1,dfs(y); } signed main(){ freopen("wound.in","r",stdin); freopen("wound.out","w",stdout); readx(n),readx(c); FOR(i,1,n)readx(w[i]); FOR(i,1,n)readx(v[i]); int fr;FOR(i,2,n)readx(fr),add(fr,i); dfs(1); FOR(i,1,n)w[i] += c*dep[i],v[i] += c*dep[i]; std::sort(w+1,w+1+n),std::sort(v+1,v+1+n); ll ans = 0; FOR(i,1,n)ans+=std::abs(w[i]-v[i]); output(ans),putchar(10); return 0; } ``` ### t2 给定 $n$ 个节点,$m$ 条边的有向图,有 $3$ 种操作,每个节点有 $a_i,b_i$ 两种点权,$a_i$ 给定初始值,$b_i$ 初始为 $0$。 操作 $0$: 给定 $l,r$,求 $\sum_{i=l}^{r}a_i$。 操作 $1/2$: 给定 $l,r,c$,依次访问 $l$ 到 $r$ 的节点。 每访问一个节点 $u$,将每个点按边权从小到大的顺序遍历出边,然后将其出点加入到序列 $S$ 的末尾,$S$ 可重。 然后将 $i$ 置为 $1$,$k$ 置为 $0$,执行完以下操作后,$i \gets i+c$;如果 $i$ 超过序列 $S$ 的元素数后,结束此次操作。 如果 $a_i = 0$,不进行任何操作; 如果 $a_i \not = 0$: $k \gets k+1$ 如果总操作是操作 $1$:$a_i \gets \max(0,a_i-b_i)$,$b_i \gets b_i+k$。 如果总操作是操作 $2$:$a_i \gets \max(0,a_i-kb_i)$,$b_i \gets b_i+1$。 上述两种操作均是顺次进行的。 $q$ 次操作。 $1 \leq n,q \leq 2 \times 10^5$,$1 \leq m \leq 4 \times 10^5$,$1 \leq a_i \leq 10^9$。 时间限制 $8s$,空间 $512 MB$。 ![](https://cdn.luogu.com.cn/upload/image_hosting/gqbsw3zd.png) ![](https://cdn.luogu.com.cn/upload/image_hosting/mnubfirv.png) ![](https://cdn.luogu.com.cn/upload/image_hosting/n6bwxvu0.png) 至多进行 $O(nV^{\frac{1}{3}})$ 次有效的的 $1,2$ 操作,$V$ 为 $a_i$ 最大值。 考虑一次操作,视其对前 $V^{\frac{1}{3}}$ 个元素暴力操作,记一个 $b_i$ 在 $V^{\frac{1}{3}}$ 次操作之后被操作的次数为 $n$,则 $a_i$ 至少被减去了 $V^{\frac{1}{3}}\dfrac{(1+n)n}{2}$,所以每个元素至多贡献 $V^{\frac{1}{3}}$ 后面操作。 考虑如何跳过没有值的位置。 对 $c$ 分块,$c \lt B$ 对每个位置维护 $O(B)$ 个并查集,$c \geq B$ 暴力跳。 能平衡到 $O(m \sqrt q)$,但是空间开不下,块长被迫缩小。 修改考虑用分块 $O(1)$ 修,$O(\sqrt n)$ 查询。 总时间复杂度 $O(nV^{\frac{1}{3}}+m \sqrt q+q \sqrt n)$ 这样。 ```cpp #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cctype> #include <vector> #include <cassert> // #define ll __int128 #define ll long long #define FOR(i,a,b) for(int i = (a);i <= (b);++i) #define REP(i,a,b) for(int i = (a);i >= (b);--i) // #define GO(x) for(int i = h[x],y = e[i];i;y = e[i=ne[i]]) #define GO(x) for(auto &y:e[x]) #define ve std::vector #define pb push_back static char stkk[200]; template<typename T>inline void output(T x){ if(!x)return putchar('0'),void(); if(x<0)x = ~x+1,putchar('-'); int top = 0; for(;x;stkk[++top]=x%10^48,x/=10); for(;top;putchar(stkk[top--])); } template<typename T>inline void readx(T &x){ x = 0;int y = 1;char c = getchar(); for(;c<48||c>58;c = getchar())if(c=='-')y = -1; for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48); x *= y; } template<typename T1,typename T2>inline T1 maxi(T1 x,T2 y){return x<y?y:x;} template<typename T1,typename T2>inline T1 mini(T1 x,T2 y){return x<y?x:y;} const int N = 2e5+10,M = 4e5+10+150,B = 150,G = 500,nf = 1e9; static int n,m,q; static int a[N],b[N]; static ll su[G]; static int fa[M][B],c[M]; static int l[N],r[N],tp,stk[N],tktp,po[N],st[G],ed[G],tot,len; static ve<int> e[N],id[N]; int find(int x,int now){return fa[x][now]^x?fa[x][now]=find(fa[x][now],now):x;} inline ll qy(int l,int r){ int L = po[l],R = po[r]; ll ans = 0; if(L^R){ FOR(i,L+1,R-1)ans+=su[i]; FOR(i,l,ed[L])ans+=a[i]; FOR(i,st[R],r)ans+=a[i]; } else FOR(i,l,r)ans+=a[i]; return ans; } inline void work1(int id,int k){a[id] = maxi(0,a[id]-b[id]),b[id] = mini(b[id]+k,nf);} inline void work2(int id,int k){a[id] = maxi(0,a[id]-1ll*k*(b[id]++));} signed main(){ freopen("power.in","r",stdin); freopen("power.out","w",stdout); readx(n),readx(m),readx(q); len = std::sqrt(n),tot = (n-1)/len+1; FOR(i,1,tot)st[i] = ed[i-1]+1,ed[i] = i*len; ed[tot] = n; FOR(i,1,n)po[i] = (i-1)/len+1; FOR(i,1,n)readx(a[i]),su[po[i]]+=a[i]; int u,v;FOR(i,1,m)readx(u),readx(v),e[u].pb(v); FOR(x,1,n){ l[x] = tp+1; GO(x)c[++tp] = y,id[y].pb(tp); r[x] = tp; } FOR(j,1,B-1)FOR(i,1,tp+B)fa[i][j] = i; for(int op,Pl,Pr,jp,k;q--;){ readx(op),readx(Pl),readx(Pr); if(!op)output(qy(Pl,Pr)),putchar(10); else{ readx(jp);k = 0; for(int t = l[Pl],id;t <= r[Pr];jp>=B?(t+=jp):(t=find(t+jp,jp)))if(a[c[t]]){ ll pre = a[id=c[t]]; if(op&1)work1(id,++k); else work2(id,++k); if(!a[id])stk[++tktp] = id; su[po[id]]+=a[id]-pre; } FOR(tid,1,tktp)for(auto &i:id[stk[tid]])FOR(j,1,B-1)fa[i][j] = i+j,tp+1; tktp = 0; } } return 0; } ``` ### t3 $T$ 组数据。 有一个 $n$ 个节点的完全图,给定其中 $m$ 条合法的简单路径。 记 $p_{a,x}$ 表示在 $a$ 路径中 $x$ 点的下标。 两条路径 $a,b$ 不一致,当且仅当存在两个点 $x,y$,它们在 $a,b$ 中均有出现,并且满足 $(p_{a,x}-p_{a,y})(p_{b,x}-p_{b,y}) \gt 0$,同时两条路径中 $x,y$ 两点之间的子段不完全相同。 比如: $4,1,2,3$ 和 $5,6,4,1,2$ 是一致的。 $1,2,3$ 和 $1,3,2$ 是不一致的。 判断是否不存在两条路径不一致。 $1 \leq \sum n,\sum m,\sum k \leq 3 \times 10^5$,其中 $k$ 是路径长度。 形式化题意:给定 $m$ 个序列,每个序列的元素在 $[1,n]$ 以内,并且不重复。 两个序列 $a,b$ 同构,等价于对于所有其中在 $a,b$ 中均有出现的元素 $x,y$,如果它们满足 [$p_{a,x} \lt p_{a,y}$ 并且 $p_{b,x} \lt p_{b,y}$] 或者满足 [$p_{a,x} \gt p_{a,y}$ 并且 $p_{b,x} \gt p_{b,y}$],不妨设是满足第 $1$ 种条件,均有 $a$ 序列中 $[p_{a,x},p_{a,y}]$ 的子段与 $b$ 序列中 $[p_{b,x},p_{b,y}]$ 的子段完全一样。 判断所有序列两两之间是否均同构。 存在一种 $\min(len_a,len_b)$ 判断两条路径是否一致的方法。 就是从左往右扫描一条路径 $a$,对于第一个出现的相同节点 $c$,记录 $p_{a,c}-p_{b,c}$ 的值以及 $p_{b,c}$; 如果后续有扫描到相同节点 $d$,并且 如果 $p_{a,d} \lt p_{a,c}$ 那不用管它。 否则如果: $p_{a,d}-p_{b,d} \not= p_{a,c}-p_{b,c}$ 两条路径肯定不一致; 否则要求 $c,d$ 之间都没有出现过 $p_{a,d} \lt p_{a,c}$ 以及是公共点。 说人话就是对 $a,b$ 中共同出现的元素之间两两连边(思维层面的),如果存在两条分别从元素 $x$ 和元素 $y$ 出发的边,满足二者不相交,那么这两条边的斜率一定要一致,并且 $[p_{a,x},p_{a,y}]$ 中间的点必须都有连边,并且斜率一致,具体实现可以参考代码或者上述说明。 考虑对于路径长度 $len$ 分治。 当 $len \gt B$ 时,暴力检查其与其它所有路径是否一致,检查一条边的时间复杂度是 $O(\sum k)$ 的,总时间复杂度 $O(\dfrac{k^2}{B})$。 否则考虑两个路径 $a,b$ 的 $x,y$ 中间子段相等的限制,等价于 $a_{p_{a,x}+1}=b_{p_{b,x}+1}$,发现其具有递归性。 考虑暴力维护每条路径 $a$ 中的三元组 $(a_x,a_{x+1},a_y)$,$(x \lt y)$。 不存在两条路径不一致,等价于不存在三元组满足第一项与第三项对应相等的同时,第二项不等。 记 $t$ 为 $len \leq B$ 的路径的平均长度,则时间复杂度约为 $O(t^2 \dfrac{k}{t}) = O(tk)$,$t$ 最大取 $B$。 所以 $B$ 取 $\sqrt k$ 即可。 时间复杂度 $O(k \sqrt k)$。 ```cpp #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cctype> #include <vector> #define ll long long #define FOR(i,a,b) for(int i = (a);i <= (b);++i) #define REP(i,a,b) for(int i = (a);i >= (b);--i) #define GO(x) for(int i = h[x],y = e[i];i;y = e[i=ne[i]]) #define ve std::vector #define pb push_back static char stkk[200]; template<typename T>inline void output(T x){ if(!x)return putchar('0'),void(); if(x<0)x = ~x+1,putchar('-'); int top = 0; for(;x;stkk[++top]=x%10^48,x/=10); for(;top;putchar(stkk[top--])); } template<typename T>inline void readx(T &x){ x = 0;int y = 1;char c = getchar(); for(;c<48||c>58;c = getchar())if(c=='-')y = -1; for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48); x *= y; } const int N = 3e5+10,nf = 1e9+10,B = std::sqrt(N); static int len[N],n,m,npy[N],tp[N],p[N]; struct node{int x,y;}; static ve<int> s[N]; static ve<node> f[N]; inline bool check(const ve<int> &s1,int *p2,int l1){ int mi = nf,id = -nf; bool flg = 0; FOR(i,1,l1){ int now = s1[i]; int p = p2[now]; if(p){ if(id==-nf){ id = i-p; mi = p; continue; } else if(i-p==id){ if(flg)return 0; continue; } if(mi<p)return 0; if(mi==p)puts("?"),exit(0); } else if(id!=-nf)flg |= 1; } return 1; } inline void solve(){ readx(n),readx(m); FOR(i,1,n)f[i].clear(); FOR(i,1,m){ s[i].clear(); readx(len[i]); s[i].resize(len[i]+2); FOR(j,1,len[i]){ readx(s[i][j]); } } FOR(i,1,m){ if(len[i]>=B){ FOR(j,1,len[i])p[s[i][j]] = j; FOR(j,1,m)if(i^j)if(!check(s[j],p,len[j])){ FOR(j,1,len[i])p[s[i][j]] = 0; return puts("NO"),void(); } FOR(j,1,len[i])p[s[i][j]] = 0; } else{ FOR(j1,1,len[i]){ FOR(j2,j1+1,len[i]){ f[s[i][j1]].pb((node){s[i][j1+1],s[i][j2]}); } } } } FOR(i,1,n){ for(auto t:f[i]){ if(!npy[t.y])npy[t.y] = t.x; else if(npy[t.y]^t.x){ for(auto &t:f[i])npy[t.y] = 0; return puts("NO"),void(); } } for(auto &t:f[i])npy[t.y] = 0; } puts("YES"); } signed main(){ freopen("mismatch.in","r",stdin); freopen("mismatch.out","w",stdout); int t;for(readx(t);t--;solve()); return 0; } ``` ### t4 [P14035 [PAIO 2025] GCD](https://www.luogu.com.cn/problem/P14035) 给定长度为 $n$ 的序列和常数 $k$ 和 $V$,求: $\sum_{x=0}^{V}\sum_{i=1}^{n}\sum_{j=i+1}^{n}((\gcd\{a_1,a_2,\cdots,a_i,a_j,a_{j+1},\cdots,a_{n}\})^k \text{xor} x)(a_i+a_j)$ $1 \leq n \leq 5 \times 10^5,1 \leq k \leq 100,1 \leq V \leq 10^9$。 考虑前后缀 $gcd$ 至多有 $\log V$ 个不同的段,所以至多有 $\log^2 V$ 种不同 $\gcd$ 组合。 后面的 $a_i,a_j$ 用 $a_i$,$ia_i$ 的前缀和 和 $(n-i+1)a_i$ 的后缀和维护一下。 $\gcd$ $k$ 次方后亦或 $[0,V]$ 内的 $x$,可以按位考虑贡献。 高于 $30$ 的位一定会被贡献,低于 $30$ 的位考虑其与 $x$ 该位会有多少贡献。 ```cpp #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cctype> #include <cstring> #include <ctime> #define ll long long #define FOR(i,a,b) for(int i = (a);i <= (b);++i) #define REP(i,a,b) for(int i = (a);i >= (b);--i) #define GO(x) for(int i = h[x],y = e[i];i;y = e[i=ne[i]]) static char stkk[200]; template<typename T>inline void output(T x){ if(!x)return putchar('0'),void(); if(x<0)x = ~x+1,putchar('-'); int top = 0; for(;x;stkk[++top]=x%10^48,x/=10); for(;top;putchar(stkk[top--])); } template<typename T>inline void readx(T &x){ x = 0;int y = 1;char c = getchar(); for(;c<48||c>58;c = getchar())if(c=='-')y = -1; for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48); x *= y; } const int N = 5e5+10,mo = 998244353,G = 50+10,base = (1ll<<30)-1; static int p[N],s[N],x,pa[N],a[N],n,k,V,tpa[N],tsa[N]; static ll pw[G]; static int r[N],l[N]; inline ll ksm(int a,int b){int c = 1;for(;b;b>>=1,a = 1ll*a*a%mo)if(b&1)c = 1ll*c*a%mo;return c;} inline ll ksm_aya(int a,int b){int c = 1;for(;b;b>>=1,a = (1ll*a*a)&base)if(b&1)c = (1ll*c*a)&base;return c;} inline void prepw(int n){pw[0] = 1;FOR(i,1,n)pw[i] = pw[i-1]<<1;} inline int ji1(int wei,int x){return ((x+1)/pw[wei+1])*pw[wei]+std::min((x+1)%pw[wei+1],pw[wei]);} inline int cal(int now,int x){ int ans = 0,len = std::max(now?std::__lg(now):0,x?std::__lg(x):0); FOR(i,0,len){ bool p = now&pw[i]; int tmp = ji1(i,x); if(!p)tmp = x+1-tmp; ans = (0ll+ans+1ll*pw[i]*tmp%mo)%mo; } return ans; } static int pl[N],tpl,pr[N],tpr; inline int work1(int l1,int l2,int r1,int r2){return (1ll*(pa[l2]-pa[l1-1])*(r2-r1+1)%mo+1ll*(pa[r2]-pa[r1-1])*(l2-l1+1)%mo)%mo;} inline int work2(int l1,int l2,int r1,int r2){ r1 = std::max(l1,r1); ll ans1 = (tpa[r2]-tpa[r1-1])-1ll*l1*(pa[r2]-pa[r1-1])%mo; l2 = std::min(l2,r2); int tl1 = std::max(l1,r1); ll ans2 = (tsa[tl1]-tsa[l2+1])-1ll*(n-r2+1)*(pa[l2]-pa[tl1-1])%mo; ans2 = (0ll+ans2+1ll*(pa[tl1-1]-pa[l1-1])*(r2-r1+1)%mo)%mo; return (ans1+ans2)%mo; } signed main(){ freopen("wave.in","r",stdin); freopen("wave.out","w",stdout); prepw(40); readx(n),readx(k),readx(V); FOR(i,1,n)readx(a[i]); FOR(i,1,n)p[i] = std::__gcd(a[i],p[i-1]),pa[i] = (0ll+pa[i-1]+a[i])%mo,tpa[i] = (0ll+tpa[i-1]+1ll*i*a[i]%mo)%mo; REP(i,n,1)s[i] = std::__gcd(a[i],s[i+1]),tsa[i] = (0ll+tsa[i+1]+1ll*(n-i+1)*a[i]%mo)%mo; r[n+1] = n+1; REP(i,n,1)r[i] = s[i]==s[i+1]?r[i+1]:i; FOR(i,1,n)l[i] = p[i]==p[i-1]?l[i-1]:i; for(int i = 2,j = 1;i <= n+1;i++){ if(p[i]!=p[i-1])pl[++tpl] = j,j = i; } for(int i = n-1,j = n;i >= 0;--i){ if(s[i]!=s[i+1])pr[++tpr] = j,j = i; } pl[tpl+1] = n+1; int ans = 0; FOR(i,1,tpl){ int l1 = pl[i],l2 = pl[i+1]-1; FOR(j,1,tpr){ int r1 = pr[j+1]+1,r2 = pr[j]; if(r2<=l1)break; ll now = std::__gcd(p[l1],s[r1]); ll lyc = ksm_aya(now,k),tmp = (0ll+ksm(now,k)-lyc)%mo; ll fnl = (1ll*tmp*(V+1)%mo+cal(lyc,V))%mo; tmp = 0; if(r1>l2)tmp = work1(l1,l2,r1,r2); else if(r2<=l2)tmp = work2(l1,l2,r1,r2); else tmp = (0ll+work1(l1,l2,l2+1,r2)+work2(l1,l2,r1,l2))%mo; ans = (0ll+ans+1ll*tmp*fnl%mo)%mo; } } output((0ll+ans+mo)%mo),putchar(10); return 0; } ```

题目列表