zoukankan      html  css  js  c++  java
  • [大山中学模拟赛] 2016.9.17

    这一场比上一场还吃屎 只back回三道题 第四题表示被吓尿不会back

    Sonya and Problem Wihtout a Legend

    第一题就出了codeforce #371 div1 的第三题我去 比赛的时候一看到这种题压根就是跳 肯定是什么想法题 但是的话这道题的想法好像后来听题解没这么重

    就是把严格上升改为不上升 然后的话这些数记录下来 因为以后不管哪个数变都会变成这些数 理由很简单 在中间的不用变 在上面的变下来 在下面的变上去 不用变多不用变少

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    #define Maxn 3010
    using namespace std;
    typedef long long LL;
    LL A[Maxn]; LL N; LL B[Maxn];
    LL F[Maxn][Maxn];
    int main()
    {
      //freopen("a.in","r",stdin);
      //freopen("a.out","w",stdout);
      scanf("%I64d",&N); for(LL i=1;i<=N;i++) scanf("%I64d",&A[i]),A[i]-=i,B[i]=A[i];
      sort(B+1,B+N+1); LL len=unique(B+1,B+N+1)-(B+1); memset(F,63,sizeof(F));
      for(LL i=1;i<=len;i++) F[1][i]=abs(A[1]-B[i]),F[1][i]=min(F[1][i],F[1][i-1]);
      for(LL i=2;i<=N;i++)
      {
        for(LL j=1;j<=len;j++)
        {
          F[i][j]=min(F[i][j],F[i-1][j]+abs(A[i]-B[j]));
          F[i][j]=min(F[i][j],F[i][j-1]);
        }
      }
      return printf("%I64d
    ",F[N][len]),0;
    }
    /*
    7
    2 1 5 11 5 9 11
    */
    View Code

    救灾

    第二题是contest hunter上CH round #50的第三题 这里顺便说一句 ljm的比赛来源好广啊 被虐的好惨啊

    这道题感觉很厉害 但听完题解后感觉题解的tree dp有点玄学

    F1[x][i] 表示这个点的管辖者在子树下面且距离到i

    F2[x][i] 表示这个点的管辖者在子树上面且距离到i

    G[x]=min(F1[x][]);

    然后转移就是:

    F1[x][i]=min(F1[son[x]][i-1]+C[i]+sigma(G[otherson[x]],F2[otherson[x]][i+1]))

    什么意思呢 就是因为是下面管我的 所以我要找一个孩子使得它子树下面管我 然后其他孩子要不是下面管 要不是上面管,反正我只要这个子树的花费

    F2[x][i]=C[i]+sigma(min(F2[son[x]][i+1],G[son[x]])

    这个的意思是现在我是上面管的 只要加上我现在的花费 然后下面子树的花费也传上来就好了

    但是有个问题就是 如果F1[x][i]是在下面有i管 但是实际上是由另外一个管怎么办呢 其实没关系 因为另外一个管的自然会有一个更好的状态

    然后还要优化一下F1的来做 自己建的时候F1就直接把下面的传上来就好了 加上费用 F2也是如此

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #define Maxn 200010
    using namespace std;
    const int inf=1e9;
    int N,C[Maxn];
     
    struct node{int x,y,next;}edge[Maxn*2]; int len,first[Maxn];
    void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
    
    int prime[Maxn],pri,phi[Maxn]; bool v[Maxn];
    void PHI()
    {
      memset(v,1,sizeof(v)); v[0]=v[1]=0; pri=0;
      for(int i=2;i<=N;i++)
      {
        if(v[i]){prime[++pri]=i; phi[i]=i-1;}
        for(int j=1;(j<=pri)&&(prime[j]*i<=N);j++)
        {
          v[i*prime[j]]=0;
          if(i%prime[j]) phi[i*prime[j]]=phi[i]*phi[prime[j]];
          else{phi[i*prime[j]]=phi[i]*prime[j]; break;}
        }
      }
    }
    
    int T[Maxn][25]; int F1[Maxn][25]; int F2[Maxn][25]; int G[Maxn]; int sum[Maxn];
    void Dfs(int x,int fa)
    {
      if(first[x]==-1)
      {
        F1[x][0]=F2[x][0]=G[x]=C[0];
        for(int i=1;i<=19;i++) F2[x][i]=C[i];
        return ;
      }
      for(int k=first[x];k!=-1;k=edge[k].next)
      {
        int y=edge[k].y;
        if(y!=fa) Dfs(y,x);
      }
      for(int i=0;i<=19;i++) sum[i]=0;
      for(int d=0;d<=19;d++)
      {
        for(int k=first[x];k!=-1;k=edge[k].next)
        {
          int y=edge[k].y;
          if(y!=fa) {T[y][d]=min(G[y],F2[y][d+1]); sum[d]+=T[y][d];}
        }
      }
      for(int d=1;d<=19;d++)
      {
        for(int k=first[x];k!=-1;k=edge[k].next)
        {
          int y=edge[k].y;
          if(y!=fa) F1[x][d]=min(F1[x][d],C[d]+F1[y][d-1]+sum[d]-T[y][d]);
        }
      }F1[x][0]=min(F1[x][0],C[0]+sum[0]);
      for(int d=0;d<=19;d++)
      {
        for(int k=first[x];k!=-1;k=edge[k].next)
        {
          int y=edge[k].y;
          if(y!=fa) F2[x][d]=min(F2[x][d],C[d]+sum[d]);
        }
      }
      for(int i=0;i<=19;i++) G[x]=min(G[x],F1[x][i]);
    }
    int main()
    {
      scanf("%d%d",&N,&C[0]);
      for(int i=1;i<N;i++) scanf("%d",&C[i]);
      
      PHI(); len=0; memset(first,-1,sizeof(first));
      for(int i=2;i<=N;i++) ins(phi[i],i);
      
      memset(F1,63,sizeof(F1)); memset(F2,63,sizeof(F2)); memset(G,63,sizeof(G)); memset(T,63,sizeof(T));
      Dfs(1,0);
      return printf("%d
    ",G[1]),0;
    }
    
    /*
    3 3
    1 2
    */
    View Code

     Express

    这道题贼6 bzoj三倍经验

    首先我们考虑选一个点的情况 肯定要选重心 而重心就是所有子节点的子树到达重心的最大的花费最小(到后面可以简单的证明一下)

    然后的话我们考虑两个点的情况 两个点他们所管辖的区域一定是一个联通块 这个很显然

    那么两个联通块之间 有且只有一条边相连 所以的话我们枚举每一条边 在两个端点的左右边分别找重心

    当然树这么大怎么找 我们其实可以知道 重心只会在重链上 与层数有关

    对于还要乘于个距离 我们先求出以1为根的所有乘上距离的花费ans(怎么做很简单吧)

    然后我们考虑砍掉一条边 当然ans要减去砍完这条边失去了多少费用

    然后目前的费用就是砍完后两边树的费用 分别以y和1为根的树

    找重心我以1为根的树为例 sum[i]为i下面所有的权值和 (没乘花费)

    那么的话转移就是 x->y ans-=sum[y]; 表示y子树下面的距离都要减1 然后ans+=sum[1]-sum[y] 表示y上面的子树的距离都加1

    然后一切就说通了 合起来就是ans+=sum[1]-2*sum[y] 所以的话最重的孩子sum[y](最重的孩子也就是sum最大的)如果大于的话sum[1]才能转移 不然的话就比最少花费要多

    所以整一棵树我们不用扫一遍然后来维护所用权值乘上边权 只要维护权值就好了 乘上的边权在转移树的重心的时候搞一下就好了

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    #include<climits>
    #define Maxn 50010
    using namespace std;
    typedef long long LL;
    struct node
    {
      LL x,y,next;
    }edge[Maxn*2]; LL len,first[Maxn];
    void ins(LL x,LL y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
    
    LL N; LL son1[Maxn],son2[Maxn]; LL size[Maxn]; LL Val[Maxn]; LL dep[Maxn]; LL fa[Maxn];
    void Dfs(LL x,LL f)
    {
      size[x]+=Val[x];
      for(LL k=first[x];k!=-1;k=edge[k].next)
      {
        LL y=edge[k].y;
        if(y!=f)
        {
          dep[y]=dep[x]+1; fa[y]=x;
          Dfs(y,x); size[x]+=size[y];
          if(size[son1[x]]<size[y]) son2[x]=son1[x],son1[x]=y;
          else if(size[son2[x]]<size[y]) son2[x]=y;
        }
      }
    }
    int main()
    {
      len=0; memset(first,-1,sizeof(first));
      scanf("%lld",&N); for(LL i=1;i<N;i++){LL x,y; scanf("%lld%lld",&x,&y); ins(x,y); ins(y,x);}
      for(LL i=1;i<=N;i++) scanf("%lld",&Val[i]);
      Dfs(1,0); LL S=0; for(LL i=2;i<=N;i++) S+=size[i]; LL Minx=LLONG_MAX;
      for(LL k=1;k<=len;k+=2)
      {
        LL x=edge[k].x; LL y=edge[k].y;
        if(dep[x]<dep[y]) swap(x,y);
        
        LL sum=S; LL u; LL v;
        for(u=fa[x];u;u=fa[u]) size[u]-=size[x],sum-=size[x];
        
        u=1; 
        while(1)
        {
          if(size[son1[u]]>size[son2[u]]&&son1[u]!=x) v=son1[u];
          else v=son2[u];
          if(size[v]*2>size[1]){sum+=size[1]-2*size[v]; u=v;}
          else break;
        }
        
        u=x;
        while(1)
        {
          v=son1[u];
          if(size[v]*2>size[x]){sum+=size[x]-2*size[v]; u=v;}
          else break;
        }
        
        Minx=min(Minx,sum);
        for(u=fa[x];u;u=fa[u]) size[u]+=size[x];
      }
      return printf("%lld
    ",Minx),0;
    }
    /*
    5
    1 2
    1 3
    3 4
    3 5
    5 7 6 5 4
    N<=50000 W<=100
    */
    View Code
  • 相关阅读:
    实验 1:Mininet 源码安装和可视化拓扑工具
    ORACLE 数据库异常关闭处理办法
    Tomcat安装及配置教程
    关于Eclipse无server选项的解决方法
    2020软件工程作业02
    2020软件工程作业01
    C语言II作业01
    C语言总体概览
    C语言寒假大作战04
    C语言寒假大作战03
  • 原文地址:https://www.cnblogs.com/wohenshuai/p/5883778.html
Copyright © 2011-2022 走看看