====== 2020ICPC·南京站 ====== [[https://ac.nowcoder.com/acm/contest/5666|比赛链接]] ===== M.Monster Hunter ===== [[https://ac.nowcoder.com/acm/contest/10272/M|链接]] ==== 题意 ==== 给定以 $1$ 为根的 $n$ 阶的点权树,假定可以用无费用无限制删去其中 $k$ 个结点,问删除剩余结点的最小费用。 其中,对剩余的所有结点,删除它之前需要删除它的父结点,同时删除它的费用为它的点权 $+$ 当前的所有儿子结点的费用的和。 要求输出 $k=0,1\cdots n$ 时的答案。 ==== 题解 ==== 易知确认无费用无限制删除的结点后答案已经固定。考虑 $dp(u,k,0/1)$ 表示以结点 $u$ 的子树中有代价删除 $k$ 个结点的最小费用。 其中 $dp(u,k,0)$ 表示无代价删除 $u$ 结点, $dp(u,k,1)$ 表示有代价删除 $u$ 结点。不难得到状态转移方程 $$ dp(u,i+j,0)=\min(dp(u,i+j,0),dp(u,i,0)+\min(dp(v,j,0),dp(v,j,1))) $$ $$ dp(u,i+j,1)=\min(dp(u,i+j,0),dp(u,i,1)+\min(dp(v,j,0),dp(v,j,1)+w_v)) $$ 注意优化转移方式,结合代码,不难发现转移过程等效于枚举每个点对的 $\text{LCA}$,所以复杂度为 $O(n^2)$。 const int MAXN=2e3+5; const LL Inf=1e18; LL dp[MAXN][MAXN][2]; int head[MAXN],edge_cnt,w[MAXN]; struct Edge{ int to,next; }edge[MAXN]; void Insert(int u,int v){ edge[++edge_cnt]=Edge{v,head[u]}; head[u]=edge_cnt; } int sz[MAXN]; void dfs(int u){ dp[u][0][0]=0; dp[u][1][1]=w[u]; sz[u]=1; for(int i=head[u];i;i=edge[i].next){ int v=edge[i].to; dfs(v); for(int j=sz[u];j>=0;j--) _rep(k,1,sz[v]){ dp[u][j+k][0]=min(dp[u][j+k][0],dp[u][j][0]+min(dp[v][k][0],dp[v][k][1])); dp[u][j+k][1]=min(dp[u][j+k][1],dp[u][j][1]+min(dp[v][k][0],dp[v][k][1]+w[v])); } sz[u]+=sz[v]; } } int main() { int T=read_int(); while(T--){ int n=read_int(); _rep(i,1,n)_rep(j,1,n)dp[i][j][0]=dp[i][j][1]=Inf; edge_cnt=0; _rep(i,1,n)head[i]=0; _rep(i,2,n)Insert(read_int(),i); _rep(i,1,n)w[i]=read_int(); dfs(1); for(int i=n;i;i--) space(min(dp[1][i][0],dp[1][i][1])); enter(0); } return 0; } ===== J.Just Another Game of Stones ===== [[https://ac.nowcoder.com/acm/contest/10272/J|链接]] ==== 题意 ==== 给定一个长度为 $n$ 的序列,接下来 $q$ 个操作。 操作 $1$ 为区间 $\max$。 操作 $2$ 为给定数量分别和序列 $[l,r]$ 对应相等的石头堆,以及一个额外的数量为 $x$ 的石头堆,进行石头游戏。 对每个操作 $2$,询问先手时第一步有几种操作可以保证必胜。 ==== 题解 ==== 对于一堆异或和为 $S$ 的石头堆,每个堆当且仅当取 $a_i-(S\oplus a_i)$ 个石头才能保证余下石头异或和为 $0$。 这需要保证 $a_i-(S\oplus a_i)\gt 0$。若 $S=0$,显然无解。 不难发现若 $a_i$ 的二进制表示在 $S$ 的最高位为 $1$ 则 $a_i\gt (S\oplus a_i)$,相反则有 $a_i\lt (S\oplus a_i)$。 于是只需要维护区间的二进制各位中的 $1$ 的个数即可,时间复杂度 $O((n+q)\log n\log v)$。 const int MAXN=2e5+5,MAXB=30; int a[MAXN]; int lef[MAXN<<2],rig[MAXN<<2],minv[MAXN<<2],minc[MAXN<<2],secv[MAXN<<2],lazy[MAXN<<2]; struct Node{ int bitnum[MAXB]; Node(int v=0){ _for(i,0,MAXB) bitnum[i]=(v>>i)&1; } Node operator + (const Node &b)const{ Node c; _for(i,0,MAXB) c.bitnum[i]=bitnum[i]+b.bitnum[i]; return c; } Node operator += (const Node &b){ _for(i,0,MAXB) bitnum[i]+=b.bitnum[i]; return *this; } }sum[MAXN<<2]; void push_up(int k){ sum[k]=sum[k<<1]+sum[k<<1|1]; if(minv[k<<1]==minv[k<<1|1]){ minv[k]=minv[k<<1]; minc[k]=minc[k<<1]+minc[k<<1|1]; secv[k]=min(secv[k<<1],secv[k<<1|1]); } else if(minv[k<<1]>i)&1) sum[k].bitnum[i]-=minc[k]; if((v>>i)&1) sum[k].bitnum[i]+=minc[k]; } minv[k]=lazy[k]=v; } void push_down(int k){ if(~lazy[k]){ push_tag(k<<1,lazy[k]); push_tag(k<<1|1,lazy[k]); lazy[k]=-1; } } void build(int k,int L,int R){ lef[k]=L,rig[k]=R,lazy[k]=-1; int M=L+R>>1; if(L==R){ sum[k]=Node(a[M]); minv[k]=a[M]; secv[k]=1<=v)return; if(L<=lef[k]&&rig[k]<=R&&secv[k]>v) return push_tag(k,v); push_down(k); int mid=lef[k]+rig[k]>>1; if(mid>=L)update(k<<1,L,R,v); if(mid>1; Node s=Node(); if(mid>=L)s+=query_sum(k<<1,L,R); if(mid=0;i--){ if(temp.bitnum[i]&1){ maxb=i; break; } } if(maxb==-1) enter(0); else enter(temp.bitnum[maxb]); } } return 0; }