用户工具

站点工具


2020-2021:teams:legal_string:jxm2001:contest:2020_icpc_南京

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
2020-2021:teams:legal_string:jxm2001:contest:2020_icpc_南京 [2021/01/14 20:30]
jxm2001 创建
2020-2021:teams:legal_string:jxm2001:contest:2020_icpc_南京 [2021/01/14 20:32] (当前版本)
jxm2001
行 2: 行 2:
  
 [[https://​ac.nowcoder.com/​acm/​contest/​5666|比赛链接]] [[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)$。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +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;
 +}
 +</​code>​
 +</​hidden>​
 +
 +===== 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)$。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +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]<​minv[k<<​1|1]){
 + minv[k]=minv[k<<​1];​
 + minc[k]=minc[k<<​1];​
 + secv[k]=min(secv[k<<​1],​minv[k<<​1|1]);​
 + }
 + else{
 + minv[k]=minv[k<<​1|1];​
 + minc[k]=minc[k<<​1|1];​
 + secv[k]=min(minv[k<<​1],​secv[k<<​1|1]);​
 + }
 +}
 +void push_tag(int k,int v){
 + if(v<​=minv[k])return;​
 + _for(i,​0,​MAXB){
 + if((minv[k]>>​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<<​MAXB;​
 + minc[k]=1;​
 + return;
 + }
 + build(k<<​1,​L,​M);​
 + build(k<<​1|1,​M+1,​R);​
 + push_up(k);​
 +}
 +void update(int k,int L,int R,int v){
 + if(minv[k]>​=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<​R)update(k<<​1|1,​L,​R,​v);​
 + push_up(k);​
 +}
 +Node query_sum(int k,int L,int R){
 + if(L<​=lef[k]&&​rig[k]<​=R)return sum[k];
 + push_down(k);​
 + int mid=lef[k]+rig[k]>>​1;​
 + Node s=Node();
 + if(mid>​=L)s+=query_sum(k<<​1,​L,​R);​
 + if(mid<​R)s+=query_sum(k<<​1|1,​L,​R);​
 + return s;
 +}
 +int main()
 +{
 + int n=read_int(),​q=read_int();​
 + _rep(i,​1,​n)a[i]=read_int();​
 + build(1,​1,​n);​
 + while(q--){
 + int op=read_int(),​l=read_int(),​r=read_int(),​x=read_int();​
 + if(op==1)
 + update(1,​l,​r,​x);​
 + else{
 + Node temp=query_sum(1,​l,​r)+Node(x);​
 + int maxb=-1;
 + for(int i=MAXB-1;​i>​=0;​i--){
 + if(temp.bitnum[i]&​1){
 + maxb=i;​
 + break;
 + }
 + }
 + if(maxb==-1)
 + enter(0);​
 + else
 + enter(temp.bitnum[maxb]);​
 + }
 + }
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
  
  
2020-2021/teams/legal_string/jxm2001/contest/2020_icpc_南京.1610627450.txt.gz · 最后更改: 2021/01/14 20:30 由 jxm2001