zoukankan      html  css  js  c++  java
  • [atAGC034E]Complete Compress

    先考虑枚举最后的点,并以其为根

    首先,操作祖先-后代关系是没有意义的,因为以后必然有一次操作会操作祖先使其返回原来的位置,那么必然不如操作后代和那一个点(少一次操作)

    考虑某一次操作,总深度和恰好减2,因此若有解,操作次数为深度和的一半

    考虑dp,令$f_{k}$表示以$k$为根的子树经过若干次操作后,最小的深度和

    考虑加入一棵以$son$为根的子树,有三种情况:

    1.$f_{son}$大于之前所有子树内部节点的深度和,那么之前的子树中节点不在内部操作,全部与这棵子树抵消,剩下的深度即为$f_{k}$

    2.之前子树内部最小的答案也大于了这棵子树内部的深度和,那么用这棵子树的深度和去抵消之前的深度

    3.可以证明,一定有方案使得其能够比较完美的匹配,换言之根据奇偶性判断剩下0或1

    更具体的,记$g_{k}$表示以$k$为根的子树内部初始深度和(相对于$k$),$sz_{k}$表示以$k$为根的子树内不节点个数,转移如下($g_{k}$是不断加入子树,即记录之前所有子树内部深度和):
    $$
    f_{k}=egin{cases}(f_{son}+sz_{son})-g_{k}(g_{k}<f_{son}+sz_{son})\ f_{k}-(g_{son}+sz_{son})(f_{k}>g_{son}+sz_{son})\ (f_{k}+g_{son}+sz_{son})mod 2end{cases}
    $$
    (关于$g_{k}$的转移是$g_{k}=g_{k}+g_{son}+sz_{son}$)

    最后判定$f_{root}$即可,时间复杂度为$o(n^{2})$,可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 2005
     4 #define ll long long
     5 struct ji{
     6     int nex,to;
     7 }edge[N<<1];
     8 int E,n,x,y,ans,head[N],sz[N],g[N],f[N];
     9 char s[N];
    10 void add(int x,int y){
    11     edge[E].nex=head[x];
    12     edge[E].to=y;
    13     head[x]=E++;
    14 }
    15 void dfs(int k,int fa){
    16     sz[k]=g[k]=f[k]=0;
    17     if (s[k]=='1')sz[k]=1;
    18     for(int i=head[k];i!=-1;i=edge[i].nex)
    19         if (edge[i].to!=fa){
    20             dfs(edge[i].to,k);
    21             sz[k]+=sz[edge[i].to];
    22             if (g[k]<f[edge[i].to]+sz[edge[i].to])f[k]=f[edge[i].to]+sz[edge[i].to]-g[k];
    23             else{
    24                 if (g[edge[i].to]+sz[edge[i].to]<f[k])f[k]-=g[edge[i].to]+sz[edge[i].to];
    25                 else f[k]=((f[k]+g[edge[i].to]+sz[edge[i].to])&1);
    26             }
    27             g[k]+=g[edge[i].to]+sz[edge[i].to];
    28         }
    29 }
    30 int main(){
    31     scanf("%d%s",&n,s+1);
    32     memset(head,-1,sizeof(head));
    33     for(int i=1;i<n;i++){
    34         scanf("%d%d",&x,&y);
    35         add(x,y);
    36         add(y,x);
    37     }
    38     ans=n*n;
    39     for(int i=1;i<=n;i++){
    40         dfs(i,0);
    41         if (!f[i])ans=min(ans,g[i]/2);
    42     }
    43     if (ans==n*n)ans=-1;
    44     printf("%d",ans);
    45 }
    View Code
  • 相关阅读:
    文字对战小游戏~~~
    面向对象--类库、委托、is和as运算符、泛型集合
    推箱子
    算法训练 K好数
    用memset设置无穷大无穷小
    算法训练 关联矩阵
    未名湖边的烦恼
    数字三角形
    算法训练 最大最小公倍数
    算法训练 区间k大数查询
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14365633.html
Copyright © 2011-2022 走看看