A
略
B
略
C
略
D(构造分形)
题意:
给出一个由n个点的组成的树,你可以加一些点形成一个更大的树。对于新树中的两个点i和j,如果以i为根的树与以j为根的树是同构的那么i和j颜色可以相同。问最少需要多少颜色,在颜色最少的情况下,最少需要多少叶子节点。
n<=100
分析:
根据给的样例画一画,就明白是需要把树补成一个“分形”的结构,那么离分形中心距离一样的点就是同颜色的,于是我们希望最小化离中心最大的点的距离
也就是说最少颜色一定是树的直径的一半,于是我们自然想到把直径拉出来,取中间的那个点为分形中心
但良心的样例告诉我们,这样取不一定会让叶子节点的个数最少,你可以把一条边作为分形中心,分成左右两个分形,而且这个边可能也不在直径上
考虑到n<=100,我们不妨枚举哪个点作为中心,枚举哪个边作为中心,去取一个最小值即可
时间复杂度O(n^2)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }