[关闭]
@exut 2024-10-15T01:26:58.000000Z 字数 3274 阅读 254

题解:abc370f

atcoder 题解


二分之类的思路就不讲了,这里给点不一样的 check 方法(除了倍增以外的)。


分块

请注意本部分非正解并不能过这道题。

后半部分有一个“往后跳”的过程,考虑维护。

可以联想到一个叫做弹飞绵羊的题。

于是可以考虑分块。

断环为链后分块,记录每个点跳出块所需次数和跳出块后到哪。

为所有

复杂度

但是很惨阿,这样过不了,数据范围不支持,有卡常优化大师卡过去了可以踢我一下。

给出我的愚蠢代码:

  1. #include<bits/stdc++.h>
  2. #pragma GCC optimize(2)
  3. using namespace std;
  4. #define int long long
  5. //NKlogN is easy
  6. //不会倍增,考虑分块
  7. int n,K;
  8. int a[400005];
  9. int sum[400005];
  10. int k[400005],ot[400005],wh[400005],l[400005],r[400005];
  11. int nxt[400005];
  12. int ok[400005];
  13. bool check(int X){
  14. int j=0;
  15. for(int i=1;i<=n*2;++i)
  16. {
  17. ok[i]=0;
  18. while(j<=n*2&&sum[j]-sum[i-1]<X)
  19. ++j;
  20. nxt[i]=j+1;
  21. // cerr<<nxt[i]<<" ";
  22. }
  23. for(int i=2*n;i>=1;i--){
  24. if(nxt[i]>r[k[i]]){
  25. ot[i]=1;//次数
  26. wh[i]=nxt[i];
  27. }
  28. else{
  29. ot[i]=ot[nxt[i]]+1;
  30. wh[i]=wh[nxt[i]];
  31. }
  32. }
  33. bool flg=0;
  34. for(int i=1;i<=n;i++){
  35. int fr=i,tim=0;
  36. while(1){
  37. if(tim+ot[fr]>K){
  38. // if()
  39. while(tim<K){
  40. if(fr>0 and nxt[fr]<=i+n){
  41. fr=nxt[fr];
  42. }
  43. else{fr=-1;break;}
  44. tim++;
  45. }
  46. if(fr==-1){
  47. ok[i]=0;
  48. }
  49. else{
  50. ok[i]=flg=1;//cerr<<i<<" ";
  51. }
  52. break;
  53. }
  54. else if(fr<0){
  55. break;
  56. }
  57. else{
  58. tim+=ot[fr];
  59. fr=wh[fr];
  60. }
  61. if(fr>n+i) break;
  62. }
  63. }
  64. return flg;
  65. }
  66. signed main(){
  67. ios::sync_with_stdio(0);
  68. cin.tie(0);
  69. cout.tie(0);
  70. cin>>n>>K; int sq=sqrt(n*2);
  71. for(int i=1;i<=n;i++){
  72. cin>>a[i],a[i+n]=a[i];
  73. }
  74. for(int i=1;i<=n*2;i++){
  75. sum[i]=sum[i-1]+a[i];
  76. }
  77. for(int i=1;i<=n*2;i++) l[i]=INT_MAX;
  78. for(int i=1;i<=n*2;i++){
  79. k[i]=(i-1)/sq+1;
  80. l[k[i]]=min(i,l[k[i]]);
  81. r[k[i]]=i;
  82. }
  83. int l=0,r=2000000001,mid,ans;
  84. while(l<=r){mid=l+r>>1;
  85. if(check(mid))
  86. ans=mid,l=mid+1;
  87. else r=mid-1;
  88. }
  89. check(ans);
  90. cout<<ans<<" ";
  91. ans=0;
  92. for(int i=1;i<=n;i++) ans+=!ok[i];
  93. cout<<ans;
  94. // for(int i=1;i<=n;i++)cout<<ok[i];
  95. }

带权并查集

此做法能过,并且不需要 的空间。

我们处理出每个点对应的 (含义同其他题解,双指针求出来满足这一段和大于等于 的下一个点)。顺便记录每个点会从哪些点转移过来。

  1. for(int i=1;i<=n*2;i++) FR[i].erase(FR[i].begin(),FR[i].end());
  2. for(int i=1;i<=n*2;++i)
  3. {
  4. tim[i]=0;
  5. ok[i]=0;
  6. while(j<n*2&&sum[j]-sum[i-1]<X)
  7. ++j;
  8. nxt[i]=j+1;FR[j+1].push_back(i);
  9. }

然后把所有 设为 ,其余设为自身。 跳到他的父亲所需的时间。

  1. for(int i=1;i<=n*2;i++){
  2. fa[i]=i;
  3. if(nxt[i]<=n)
  4. fa[i]=nxt[i],tim[i]=1;
  5. }

然后循环枚举起点,对于一个新的 等于他的点的 设为 设为

find 就是一个正常的带权并查集的

  1. int find(int x){
  2. if(x==fa[x])
  3. {return x;}
  4. int t=find(fa[x]);
  5. tim[x]+=tim[fa[x]];
  6. fa[x]=t;
  7. return fa[x];
  8. }

然后每次 find 一下然后看 是不是

就可以了。

  1. for(int i=1;i<=n;i++){
  2. for(auto V:FR[i+n]){
  3. fa[V]=i+n;
  4. tim[V]=1;
  5. }
  6. int pos=find(i);
  7. int tms=tim[i]+1;
  8. if(tms>K) ok[i]=flg=1;
  9. }

完整代码:(空间开的有点大)

  1. #include<bits/stdc++.h>
  2. #pragma GCC optimize(2)
  3. #pragma GCC optimize(3)
  4. using namespace std;
  5. #define int long long
  6. //NKlogN is easy
  7. //不会倍增,考虑分块
  8. //草,复杂度不对,AC47个点
  9. //考虑奇怪做法
  10. // std::mt19937 rd(time(0));
  11. int n,K;
  12. int a[1000005];
  13. int sum[1000005];
  14. int nxt[1000005];
  15. bool ok[1000005];
  16. int fa[1000005];
  17. int tim[1000005];
  18. int jpt;
  19. vector<int> FR[800005];
  20. int find(int x){
  21. if(x==fa[x])
  22. {return x;}
  23. int t=find(fa[x]);
  24. tim[x]+=tim[fa[x]];
  25. fa[x]=t;
  26. return fa[x];
  27. }
  28. bool check(int X){
  29. int j=0;
  30. for(int i=1;i<=n*2;i++) FR[i].erase(FR[i].begin(),FR[i].end());
  31. for(int i=1;i<=n*2;++i)
  32. {
  33. tim[i]=0;
  34. ok[i]=0;
  35. while(j<n*2&&sum[j]-sum[i-1]<X)
  36. ++j;
  37. nxt[i]=j+1;FR[j+1].push_back(i);
  38. }
  39. for(int i=1;i<=n*2;i++){
  40. fa[i]=i;
  41. if(nxt[i]<=n)
  42. fa[i]=nxt[i],tim[i]=1;
  43. }
  44. bool flg=0;
  45. for(int i=1;i<=n;i++){
  46. for(auto V:FR[i+n]){
  47. fa[V]=i+n;
  48. tim[V]=1;
  49. }
  50. int pos=find(i);
  51. int tms=tim[i]+1;
  52. if(tms>K) ok[i]=flg=1;
  53. }
  54. return flg;
  55. }
  56. signed main(){
  57. ios::sync_with_stdio(0);
  58. cin.tie(0);
  59. cout.tie(0);
  60. cin>>n>>K;
  61. for(int i=1;i<=n;i++){
  62. cin>>a[i],a[i+n]=a[i];
  63. }
  64. for(int i=1;i<=n*2;i++){
  65. sum[i]=sum[i-1]+a[i];
  66. }
  67. int l=1,r=2000000001,ans=-1;
  68. while(l<=r){
  69. int mid=(l+r)>>1;
  70. if(check(mid))
  71. ans=mid,l=mid+1;
  72. else r=mid-1;
  73. }
  74. check(ans);
  75. cout<<ans<<" ";
  76. ans=0;
  77. for(int i=1;i<=n;i++) ans+=!ok[i];
  78. cout<<ans;
  79. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注