• Atcoder Grand Contest 024


    A

    B

    C

    D(构造分形)

    题意:

      给出一个由n个点的组成的树,你可以加一些点形成一个更大的树。对于新树中的两个点i和j,如果以i为根的树与以j为根的树是同构的那么i和j颜色可以相同。问最少需要多少颜色,在颜色最少的情况下,最少需要多少叶子节点。

      n<=100

    分析:

      根据给的样例画一画,就明白是需要把树补成一个“分形”的结构,那么离分形中心距离一样的点就是同颜色的,于是我们希望最小化离中心最大的点的距离

      也就是说最少颜色一定是树的直径的一半,于是我们自然想到把直径拉出来,取中间的那个点为分形中心

      但良心的样例告诉我们,这样取不一定会让叶子节点的个数最少,你可以把一条边作为分形中心,分成左右两个分形,而且这个边可能也不在直径上

      考虑到n<=100,我们不妨枚举哪个点作为中心,枚举哪个边作为中心,去取一个最小值即可

      时间复杂度O(n^2)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=100;
     4 vector<int> g[maxn+5];
     5 int n,s,t;
     6 int fa[maxn+5],dep[maxn+5],a[maxn+5];
     7 long long ans;
     8 void dfs(int k,int last)
     9 {
    10     dep[k]=dep[last]+1;
    11     fa[k]=last;
    12     int d=0;
    13     for(auto u:g[k])
    14     {
    15         if(u==last) continue;
    16         dfs(u,k);
    17         ++d;
    18     }
    19     a[dep[k]]=max(a[dep[k]],d);
    20 }
    21 int main()
    22 {
    23     scanf("%d",&n);
    24     for(int i=1;i<n;++i)
    25     {
    26         int u,v;
    27         scanf("%d%d",&u,&v);
    28         g[u].push_back(v),g[v].push_back(u);
    29     }
    30     dfs(1,0);
    31     for(int i=1;i<=n;++i)
    32         if(dep[i]>dep[s]) s=i;
    33     dfs(s,0);
    34     for(int i=1;i<=n;++i)
    35         if(dep[i]>dep[t]) t=i;
    36     //printf("%d %d
    ",s,t);
    37     int ans1=(dep[t]+1)/2;
    38     printf("%d ",ans1);
    39     for(int i=1;i<=n;++i)
    40     {
    41         memset(a,0,sizeof(a));
    42         a[0]=1;
    43         dfs(i,0);
    44         long long res=1;
    45         for(int j=0;j<=n;++j)
    46             if(a[j]==0) break;else res=res*a[j];
    47         if(a[ans1]!=0) continue;
    48         if(ans==0||res<ans) ans=res;
    49     }
    50     for(int u=1;u<=n;++u)
    51         for(auto v:g[u])
    52         {
    53             memset(a,0,sizeof(a));
    54             a[0]=2;
    55             dep[u]=0;
    56             dfs(v,u);
    57             dep[v]=0;
    58             dfs(u,v);
    59             long long res=1;
    60             for(int j=0;j<=n;++j)
    61                 if(a[j]==0) break;else res=res*a[j];
    62             if(a[ans1]!=0) continue;
    63             if(ans==0||res<ans) ans=res;
    64         }
    65     printf("%lld
    ",ans);
    66     return 0;
    67 }
    View Code

    E(计数)

    题意:

      给定一个n和k,我们构造一组A0,A1,...,An

      其中Ai是一个有i个元素的数列,每个数的范围是1~k

      若Ai-1是Ai的子序列且字典序满足Ai>Ai-1,则我们称这一组A是合法的,问一共有多少种合法的A,答案对M取模。

      n,k<=300,m<=1e9

    分析:

      我们考虑第i次操作,加入一个编号为i的点,这个点的权值就是Ai中多加的数字x,把其放到Ai-1中哪个位置的前面,就把这个点的父亲连到那个点

      然后我们考虑字典序限制,x必须放到一个比x小的数字前面(如果放到相同的前面,实际上等价于放在连续段的最后一个)

      于是就变成了一个有n+1个节点的树,然后每个点的权值都比孩子的权值大,每个点的编号都比孩子的编号小,一个树和一个A是一一对应的,于是我们对这个树计数就行了

      dp[i][j]表示有i个点的树,root的权值是j情况下的方案数,那怎么转移呢?

      我们去枚举1号点所在的子树的节点个数和1号点的权值去转移

      这样是四次方的,但写出式子发现可以前缀和优化,于是就是O(n^3)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=300;
     4 typedef long long ll;
     5 int n,m,mod;
     6 ll dp[maxn+5][maxn+5],sum[maxn+5][maxn+5],c[maxn+5][maxn+5];
     7 void work(ll &a,ll b)
     8 {
     9     a=(a+b)%mod;
    10     if(a<0) a+=mod;
    11 }
    12 int main()
    13 {
    14     scanf("%d%d%d",&n,&m,&mod);
    15     c[0][0]=1;
    16     for(int i=1;i<=n+1;++i)
    17     {
    18         c[i][0]=1;
    19         for(int j=1;j<=i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    20     }
    21     for(int i=0;i<=m;++i) dp[1][i]=1;
    22     sum[1][0]=1;
    23     for(int i=1;i<=m;++i) sum[1][i]=(sum[1][i-1]+dp[1][i])%mod;
    24     for(int i=2;i<=n+1;++i)
    25     {
    26         for(int j=0;j<=m;++j)
    27             for(int k=1;k<i;++k)
    28                 work(dp[i][j],dp[i-k][j]*c[i-2][k-1]%mod*(sum[k][m]-sum[k][j])%mod);
    29         sum[i][0]=dp[i][0];
    30         for(int j=1;j<=m;++j) sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
    31     }
    32     printf("%lld
    ",dp[n+1][0]);
    33     return 0;
    34 }
    View Code

    F(DAG图dp)

    题意:

      给定一些01字符串 ,现在你找一个01字符串s,如果给定的这些01字符串里至少有m个字符串包含s作为子序列,那么s就是合法的。对于所有合法的s,找到长度最长的(在这基础上找字典序最小的)

      01字符串的给定方式见题面

    分析:

      如果我们可以求出长度<=n的所有字符串被多少个给定字符串包含作为子序列,那么这个问题就能轻松解决了

      我们如何描述一个字符串的所有子序列呢?

      我们用s[t]表示已经固定了s,然后取t中的子序列

      那么s[t]可以转移到s0[t']  s1[t']

      并且这个dag有一个性质,就是任意两个点的路径个数<=1

      所以就可以在这个dag图上进行dp

      时间复杂度O(2^n*n^2)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=20;
     4 int dp[2][20+1][1<<maxn],nx[20+1][1<<maxn][2];
     5 char s[1<<maxn];
     6 int n,m,ans,len;
     7 int main()
     8 {
     9     scanf("%d%d",&n,&m);
    10     int now=0;
    11     for(int i=0;i<=n;++i)
    12     {
    13         scanf("%s",s);
    14         for(int j=0;j<(1<<i);++j)
    15             if(s[j]=='1') dp[now][i][j]=1;
    16     }
    17     for(int i=1;i<=n;++i)
    18         for(int j=0;j<(1<<i);++j)
    19         {
    20             nx[i][j][0]=nx[i][j][1]=-1;
    21             for(int k=i-1;k>=0;--k)
    22                 if((j>>k)&1)
    23                 {
    24                     nx[i][j][1]=k;
    25                     break;
    26                 }
    27             for(int k=i-1;k>=0;--k)
    28                 if(((j>>k)&1)==0)
    29                 {
    30                     nx[i][j][0]=k;
    31                     break;
    32                 }
    33         }
    34     for(int i=0;i<n;++i)
    35     {
    36         for(int j=n-i;j>=1;--j)
    37             for(int s=(1<<(i+j))-1;s>=0;--s)
    38             {
    39                 if(!dp[now][i+j][s]) continue;
    40                 int t=nx[j][s&((1<<j)-1)][0];
    41                 if(t!=-1)
    42                     dp[now^1][i+t+1][(s>>j<<t+1)|(s&(1<<(t+1))-1)]+=dp[now][i+j][s];
    43                 t=nx[j][s&((1<<j)-1)][1];
    44                 if(t!=-1)
    45                     dp[now^1][i+t+1][(s>>j<<t+1)|(s&(1<<(t+1))-1)]+=dp[now][i+j][s];
    46             }
    47         memset(dp[now],0,sizeof(dp[now]));
    48         now^=1;
    49         for(int j=1;i+1+j<=n;++j)
    50             for(int s=0;s<1<<(i+1+j);++s)
    51                 dp[now][i+1][s>>j]+=dp[now][i+1+j][s];
    52         for(int s=(1<<(i+1))-1;s>=0;--s)
    53         {
    54             if(dp[now][i+1][s]>=m) len=i+1,ans=s;
    55         }
    56     }
    57     for(int i=len-1;i>=0;--i)
    58         if(ans&(1<<i)) printf("1");else printf("0");
    59     return 0;
    60 }
    View Code
  • 相关阅读:
    Vuejs --01 起步
    ajax利用FormData异步文件提交
    格式化JSON数据
    获取url中的参数
    xampp配置多端口访问
    自动识别移动端还是PC端
    一些常见的shell命令和git命令
    网站性能优化之减少HTTP请求
    ajax学习笔记
    canvas随笔
  • 原文地址:https://www.cnblogs.com/wmrv587/p/9084974.html
Copyright © 2020-2023  润新知