[关闭]
@xiaoziyao 2020-10-08T15:00:55.000000Z 字数 2788 阅读 919

CF1214H Tiles Placement解题报告

解题报告


CF1214H Tiles Placement解题报告:

更好的阅读体验

题意

分析

最近听了一个神仙讲了这道题,就来做一做。

一道找规律神题,现场推出了和正解等价的规律,但是还是不会

先给出结论:

时,结论显然成立。

时,我们看一看图:

距离为距离为距离为

如果,那么我们任取链上一点链上一点链上一点,使的距离为的距离为

我们令链上已经确定了染色方案,那么的染色方案递推到的方案,一定与的染色方案递推到的方案不同,于是我们推出了矛盾。

因此结论成立。

我们考虑取出直径(这里取出直径主要是为了解决第二问),求出每个点不向直径扩展的最长链和次长链,并对于直径上的点和直径外的点分类讨论:
(下面的最长链与次长链均指不向直径扩展的链)

这样,我们就判断出了是否可以进行构造。

构造方法比较显然:

首先,因为在满足上述要求下,长度大于等于的链一定都在直径上,所以我们先对直径进行染色。

我们考虑剩下的点如何染色——剩下的点一定可以从直径上的颜色地推下来。

但是有一点细节要注意:

如图,,此时假设我们选择的直径为,那么这些路径的染色搭配也要考虑。

我们可以取直径中点,直径上左边的点可以采用颜色不断加的染色策略,及右边的点可以采用颜色不断减的染色策略,这样左右两点产生的路径我们都可以满足条件了。

复杂度:

代码

代码找了好久错,结果是的递归调用了(捂脸)。

  1. #include<stdio.h>
  2. #define inf 1000000000
  3. const int maxn=200005,maxm=maxn<<1;
  4. int n,m,e,s1,s2,s,maxx,col,flg,mid;
  5. int start[maxn],to[maxm],then[maxm];//链式前向星
  6. int dep[maxn],f[maxn],ans[maxn],flen[maxn],slen[maxn];//深度,递归时的父亲,染的颜色,最长链,次长链
  7. inline void add(int x,int y){//加边
  8. then[++e]=start[x],start[x]=e,to[e]=y;
  9. }
  10. void dfs(int x,int last){
  11. f[x]=last,dep[x]=dep[last]+1;
  12. flen[x]=slen[x]=0;
  13. if(dep[x]>maxx)
  14. maxx=dep[x],s=x;
  15. for(int i=start[x];i;i=then[i]){
  16. int y=to[i];
  17. if(y==last)
  18. continue;
  19. dfs(y,x);
  20. }
  21. }
  22. void getlen(int x,int last){
  23. for(int i=start[x];i;i=then[i]){
  24. int y=to[i];
  25. if(y==last||ans[y])
  26. continue;
  27. getlen(y,x);
  28. if(flen[y]+1>flen[x])
  29. slen[x]=flen[x],flen[x]=flen[y]+1;
  30. else if(flen[y]+1>slen[x])
  31. slen[x]=flen[y]+1;
  32. if(slen[y]+1>slen[x])
  33. slen[x]=slen[y]+1;
  34. }
  35. }
  36. void solve(int x,int c,int t){//染色,其中t=1时颜色为父亲的颜色+1,t=-1时相反
  37. ans[x]=c;
  38. c=(c+t+m-1)%m+1;
  39. for(int i=start[x];i;i=then[i]){
  40. int y=to[i];
  41. if(ans[y]!=0)
  42. continue;
  43. solve(y,c,t);
  44. }
  45. }
  46. int main(){
  47. scanf("%d%d",&n,&m);
  48. for(int i=1;i<n;i++){
  49. int x,y;
  50. scanf("%d%d",&x,&y);
  51. add(x,y),add(y,x);
  52. }
  53. maxx=-inf,dfs(1,0),s1=s;//s1为直径一个端点
  54. maxx=-inf,dfs(s,0),s2=s;//s2为直径另一端点
  55. if(m==2){//特判m=2
  56. puts("Yes");
  57. for(int i=1;i<=n;i++)
  58. printf("%d%c",dep[i]%2+1,i==n? '\n':' ');
  59. return 0;
  60. }
  61. for(int i=s2;i!=0;i=f[i]){//对直径染色
  62. col=col%m+1;
  63. if((dep[s1]+dep[s2])/2==dep[i])//找到中点
  64. mid=i;
  65. ans[i]=col;
  66. }
  67. for(int i=s2;i!=0;i=f[i]){//直径上的点向它不向直径扩展的子树中求最长/次长链
  68. getlen(i,0);
  69. if(ans[i]&&flen[i]&&dep[i]+flen[i]>=m&&(dep[s2]-dep[i]+1)+flen[i]>=m){
  70. puts("No");
  71. return 0;
  72. }
  73. }
  74. for(int i=1;i<=n;i++)
  75. if(flen[i]+slen[i]+1>=m){
  76. puts("No");
  77. return 0;
  78. }
  79. for(int i=s2;i!=mid;i=f[i])//直径前半段染色
  80. solve(i,ans[i],-1);
  81. for(int i=mid;i!=0;i=f[i])//直径后半段染色
  82. solve(i,ans[i],1);
  83. puts("Yes");
  84. for(int i=1;i<=n;i++)
  85. printf("%d%c",ans[i],i==n? '\n':' ');
  86. return 0;
  87. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注