[关闭]
@y20070316 2017-02-15T06:20:12.000000Z 字数 23113 阅读 1455

POI 2010 题解汇总

OI 题解汇总



【bzoj2079】Guilds - DFS

题意

给定一张图。
给每个点进行染色:染成黑色,或染成白色。
问能否有一种方案,使得任意一个点,满足存在一个相邻的点与它不同色。


分析

我们把原图拆分成若干个连通图,原图有解,当且仅当每个连通图有解。
对于一个连通图,设它的节点数为
①当时,必定无解;
②当时,有种很棒的想法:由于每个连通图可以对应若干个分层,设每个点在层,则有


我们随便选择一个点,然后求出分层图。
对于为奇数的,染成黑色。
对于为偶数的,染成白色。
根据分层图的定义,所以上述染色方案一定是一组解。

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  4. int rd(void);
  5. const int N=262144;
  6. int n,m;
  7. int f[N],s[N];
  8. int Find(int x) {
  9. if (f[x]==x) return x;
  10. return f[x]=Find(f[x]);
  11. }
  12. int main(void) {
  13. #ifndef ONLINE_JUDGE
  14. freopen("a.in","r",stdin);
  15. freopen("a.out","w",stdout);
  16. #endif
  17. n=rd(),m=rd();
  18. F(i,1,n) {
  19. f[i]=i;
  20. s[i]=1;
  21. }
  22. F(i,1,m) {
  23. int x=rd(),y=rd();
  24. int fx=Find(x);
  25. int fy=Find(y);
  26. if (fx!=fy) {
  27. f[fx]=fy;
  28. s[fy]+=s[fx];
  29. }
  30. }
  31. F(i,1,n)
  32. if (Find(i)==i&&s[i]==1) {
  33. printf("NIE\n");
  34. return 0;
  35. }
  36. printf("TAK\n");
  37. return 0;
  38. }
  39. int rd(void) {
  40. int x=0,f=1; char c=getchar();
  41. while (!isdigit(c)) {
  42. if (c=='-') f=-1;
  43. c=getchar();
  44. }
  45. while (isdigit(c)) {
  46. x=x*10+c-'0';
  47. c=getchar();
  48. }
  49. return x*f;
  50. }

【bzoj2080】Railways - 数形结合+构建最小生成森林

题意

给定一个序列,问是否有双栈排序的方法。
若无,则输出NIE。
若有,则输出字典序最小的序列。

分析

VFK的题解写的很详细:https://pan.baidu.com/share/link?shareid=360807&uk=235772034
业界良心!

学了两个波兰语。
NIE是NO的意思。。
TAK是YES的意思。。

我们通过观察,可以得到这样一个结论:
任何时候,两个栈中的元素都是单调递减的。
证明很简单,假如元素单调递增了,由于先要弹出上面的,再弹出下面的,所以就一定不能从小到大了。

发现想象能力非常重要呢。
就是自己闭上眼睛,在脑海里画图的能力。
嗯,我确乎地看到了一幅清晰的图像。

我们可以把这个双栈排序操作看成次操作,每次插入一个元素,然后把两个栈中的一些元素弹出。

考虑最早可能在哪里弹出。
我们粗略地估计:至少要把所有比小的元素弹出后,它才能弹出。
所以设表示不比大的数的最大的坐标,即:


那么最早也只可能在步,把插入并弹出后,弹出。

那么,假如有解,是否一定能在步弹出呢?
这是肯定的,VFK的证明我并没有看懂,但是自己YY出来了。
先说一下证明的大致思路:假设有一个方案,但是不满足步弹出。我们利用方案的可行性,构造出满足条件的方案
其中,的每个元素的压入并弹出的栈与相同,只是弹出的时间不同。
我既然可以早一点弹出,那为什么不早一点弹出呢?
所以,我们得到结论:序列有双栈排序方案,当且仅当序列的第个元素在第步弹出。

我们考虑两个元素,则一定不能在步前弹出,若,则一定不能把压入同一个栈。
我们把进行连边。

现在的问题变成:对于一张图,我们对点进行2染色,问是否有一种方案,使得任一相邻两个点的颜色不一样。
是否能把原图变成二分图

波兰人在这里还扯了一下。
k栈排序问题相当于问k染色有没有解。

暴力的做法是直接DFS。
但是注意到边数,直接做会TLE。
所以考虑优化。

我们考虑构建一个原图的生成森林,对生成森林进行2染色。
那么,考虑生成森林与原图的关系,很简单:若生成森林的2染色方案用在原图无解,则原图一定无解。
总之,我们得到了这样一个流程:
【STEP 1】构建生成森林
【STEP 2】把生成森林进行2染色
【STEP 3】判定染色方案在原图有没有解

对于【STEP 2】,直接进行DFS实现,

  1. void Modified_DFS(int u) {
  2. col[u]=tot;
  3. erase(u);
  4. while (1) {
  5. get nx = find_edge(u)
  6. if (nx==NULL)
  7. break;
  8. else Modified_DFS(v);
  9. }
  10. }

对于【STEP 3】,直接模拟双栈排序进行判定,

关键的问题是【STEP 1】。

【STEP 1】
边分为两种类型,一种是后向边,一种是前向边。什么意思?
对于一条边,我们在访问的时候要找到,在访问的时候要找到
后向边:以坐标为基底,维护一棵线段树。对于点内的值最小的点及其坐标。erase的时候就删除线段树的单点。
前向边:这个区间,对应线段树的某些点。我们把线段树的每个点一个链表,表示这个区间可以取到的。我们把线段树的这些点的链表,都加上点
我们查找一个点的时候,找从线段树的根到这个点的每个链表。为了节省时间复杂度,我们只找a值最大的那个点,所以在插入的时候按照从大到小的顺序插入。
删除一个点的时候,我们只需要处理出这个点对应那些链上的节点即可。

还差一个问题。
怎么保证字典序最小?
字典序最小要在有解的前提下。
所以我们每次尽量从编号小的点开始染色就好了。


【bzoj2081】Beads - 调和级数+Hash

题意

给定一个长度为的序列
,最大化,其中为:把序列连续的个切成一段,所有段中段的不同个数。

分析

这么简单的题竟然没想到QAQ。

首先连续的个切成一段。
枚举,这是个经典的调和级数的复杂度啦。

对于一个,如何求不同的段的个数?
直接考虑字符串Hash的方法。
把所有的字符Hash值用一个Hash存起来。

关于Hash的实现,我获得了一个新的姿势:用vector<LL>写Hash。
这样写会简单很多。
但是不好调试+速度超慢是不是。。

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <vector>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. #define D(i,a,b) for (int i=(a);i>=(b);i--)
  7. #define LL unsigned long long
  8. int rd(void);
  9. const int N=262144;
  10. const int L=1000009;
  11. int n;
  12. int a[N];
  13. int m;
  14. LL pwr[N];
  15. LL h[N],rh[N];
  16. int mx;
  17. int len,lis[N];
  18. int tot;
  19. vector<LL> mp[L];
  20. LL Get(int l,int r) {
  21. return h[r]-h[l-1]*pwr[r-l+1];
  22. }
  23. LL GetR(int l,int r) {
  24. return rh[l]-rh[r+1]*pwr[r-l+1];
  25. }
  26. int Find(LL w) {
  27. int key=w%L;
  28. for (vector<LL>::iterator it=mp[key].begin();it!=mp[key].end();it++)
  29. if (*it==w)
  30. return 1;
  31. return 0;
  32. }
  33. void Ins(LL w) {
  34. int key=w%L;
  35. mp[key].push_back(w);
  36. }
  37. void Erase(LL w) {
  38. int key=w%L;
  39. mp[key].clear();
  40. }
  41. void Update(int k) {
  42. int siz=0;
  43. for (int i=0;i+k<=n;i+=k) {
  44. int l=i+1;
  45. int r=i+k;
  46. LL w=Get(l,r);
  47. LL rw=GetR(l,r);
  48. int tag=Find(w);
  49. int tagR=Find(rw);
  50. if (!tag&&!tagR)
  51. siz++;
  52. if (!tag)
  53. Ins(w);
  54. if (!tagR)
  55. Ins(rw);
  56. }
  57. for (int i=0;i+k<=n;i+=k) {
  58. int l=i+1;
  59. int r=i+k;
  60. LL w=Get(l,r);
  61. LL rw=GetR(l,r);
  62. Erase(w);
  63. Erase(rw);
  64. }
  65. if (siz>=mx) {
  66. if (siz>mx) {
  67. mx=siz;
  68. F(i,1,len)
  69. lis[i]=0;
  70. len=0;
  71. }
  72. lis[++len]=k;
  73. }
  74. }
  75. int main(void) {
  76. #ifndef ONLINE_JUDGE
  77. freopen("a.in","r",stdin);
  78. freopen("a.out","w",stdout);
  79. #endif
  80. n=rd();
  81. F(i,1,n)
  82. a[i]=rd();
  83. m=n+1;
  84. pwr[0]=1;
  85. F(i,1,n)
  86. pwr[i]=pwr[i-1]*m;
  87. F(i,1,n)
  88. h[i]=h[i-1]*m+a[i];
  89. D(i,n,1)
  90. rh[i]=rh[i+1]*m+a[i];
  91. F(k,1,n)
  92. Update(k);
  93. printf("%d %d\n",mx,len);
  94. F(i,1,len) {
  95. printf("%d",lis[i]);
  96. if (i<len)
  97. printf(" ");
  98. else printf("\n");
  99. }
  100. return 0;
  101. }
  102. int rd(void) {
  103. int x=0,f=1; char c=getchar();
  104. while (!isdigit(c)) {
  105. if (c=='-') f=-1;
  106. c=getchar();
  107. }
  108. while (isdigit(c)) {
  109. x=x*10+c-'0';
  110. c=getchar();
  111. }
  112. return x*f;
  113. }

【bzoj2082】Divine Divisor - 数论

链接

http://blog.csdn.net/Kanosword/article/details/52717273


【bzoj2083】Intelligence test - vector+二分 or 离线+链表

题意

给定长度为的最初的序列
个询问,给定长度为的序列,问是否是由删去某些字符得到的。

分析1:vector+二分

我们考虑逐个判定能否由得到。
那么,考虑一位位判定,看能否在找到,肯定尽量往前找。
考虑把每种数值列一条链表,每次在链表上二分合适的位置。

代码1:vector+二分

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <algorithm>
  4. #include <vector>
  5. using namespace std;
  6. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  7. int rd(void);
  8. const int N=1048576;
  9. int m;
  10. int a[N];
  11. vector<int> list_A[N];
  12. int n;
  13. int nk,b[N];
  14. int Run(void) {
  15. int pre=0;
  16. F(i,1,nk) {
  17. int kd=b[i];
  18. vector<int>::iterator it=upper_bound(list_A[kd].begin(),list_A[kd].end(),pre);
  19. if (it==list_A[kd].end())
  20. return 0;
  21. pre=*it;
  22. }
  23. return 1;
  24. }
  25. int main(void) {
  26. #ifndef ONLINE_JUDGE
  27. freopen("a.in","r",stdin);
  28. freopen("a.out","w",stdout);
  29. #endif
  30. m=rd();
  31. F(i,1,m)
  32. a[i]=rd();
  33. F(i,1,m)
  34. list_A[a[i]].push_back(i);
  35. n=rd();
  36. F(c,1,n) {
  37. nk=rd();
  38. F(i,1,nk)
  39. b[i]=rd();
  40. int tag=Run();
  41. if (tag)
  42. printf("TAK\n");
  43. else printf("NIE\n");
  44. }
  45. return 0;
  46. }
  47. int rd(void) {
  48. int x=0,f=1; char c=getchar();
  49. while (!isdigit(c)) {
  50. if (c=='-') f=-1;
  51. c=getchar();
  52. }
  53. while (isdigit(c)) {
  54. x=x*10+c-'0';
  55. c=getchar();
  56. }
  57. return x*f;
  58. }

分析2:离线+链表

网上基本上没有人用这种做法。
除了popoqqq的题解和少量题解。
理论上这种做法更优秀啊。。

注意这道题是多组询问。
可以离线处理,利用遍历时的连续性来加快求解。

把要查找的串的每个链头给分类。
遍历原串,每次一个类的链头全都往后移一步。

复杂度与读入同阶。
然而跑得更慢QAQ。

代码2:离线+链表

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <vector>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. int rd(void);
  7. const int N=1048576;
  8. int m;
  9. int a[N];
  10. int n;
  11. int nk[N];
  12. vector<int> g[N];
  13. vector<int>::iterator git[N];
  14. vector<int> top_chr[N];
  15. vector<int> tmp;
  16. int main(void) {
  17. #ifndef ONLINE_JUDGE
  18. freopen("a.in","r",stdin);
  19. freopen("a.out","w",stdout);
  20. #endif
  21. m=rd();
  22. F(i,1,m)
  23. a[i]=rd();
  24. n=rd();
  25. F(c,1,n) {
  26. nk[c]=rd();
  27. g[c].resize(nk[c]+1);
  28. F(i,1,nk[c])
  29. g[c][i]=rd();
  30. git[c]=g[c].begin();
  31. git[c]++;
  32. top_chr[*git[c]].push_back(c);
  33. }
  34. F(i,1,m) {
  35. int kd=a[i];
  36. for (vector<int>::iterator it=top_chr[kd].begin();it!=top_chr[kd].end();it++)
  37. tmp.push_back(*it);
  38. top_chr[kd].clear();
  39. for (vector<int>::iterator it=tmp.begin();it!=tmp.end();it++) {
  40. int id=*it;
  41. git[id]++;
  42. if (git[id]!=g[id].end())
  43. top_chr[*git[id]].push_back(id);
  44. }
  45. tmp.clear();
  46. }
  47. F(c,1,n)
  48. if (git[c]==g[c].end())
  49. printf("TAK\n");
  50. else printf("NIE\n");
  51. return 0;
  52. }
  53. int rd(void) {
  54. int x=0,f=1; char c=getchar();
  55. while (!isdigit(c)) {
  56. if (c=='-') f=-1;
  57. c=getchar();
  58. }
  59. while (isdigit(c)) {
  60. x=x*10+c-'0';
  61. c=getchar();
  62. }
  63. return x*f;
  64. }

【bzoj2084】Antisymmetry - Manacher or 二分+Hash

题意

对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。
现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。

分析

据观察,发现一个结论:具有反对称性的字符串,它的长度一定是偶数的。
证明:反设它的长度为奇数,那么对于中点,在反对称后终点为,所以不相等,矛盾。

那么,如何刻画一个串是不是反对称串?
或者说,如何内判定一个反对称串?

首先,它的长度要是偶数。
其次,对于每个位置,它的对应位置的数字与其不同,一个是0,一个是1。
这两个位置的坐标加起来为奇数,一个0,一个1。
???

我们考虑把坐标为奇数的位置亦或1。
这样,问题就变为:求长度为偶数的回文串的个数

回文串有一个重要的指标,就是它的中点。
我们枚举不在点上的回文串的中点(因为长度为偶数),进行累加答案。
每次求以该点为中点的最长回文串的长度即可。

以每个点为中点的最长回文串,有几种求法:
①二分+Hash
②Manacher
③回文自动机

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <algorithm>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. #define LL long long
  7. int rd(void);
  8. const int N=1048576;
  9. int n;
  10. char s[N];
  11. int len;
  12. char t[N];
  13. int ex[N];
  14. LL ans;
  15. int main(void) {
  16. #ifndef ONLINE_JUDGE
  17. freopen("a.in","r",stdin);
  18. freopen("a.out","w",stdout);
  19. #endif
  20. n=rd();
  21. scanf("%s",s+1);
  22. F(i,1,n)
  23. if (i&1) {
  24. if (s[i]=='0')
  25. s[i]='1';
  26. else s[i]='0';
  27. }
  28. len=n+n+2;
  29. t[0]='<';
  30. t[1]='+';
  31. t[len]='>';
  32. F(i,1,n) {
  33. t[i<<1]=s[i];
  34. t[i<<1|1]='+';
  35. }
  36. int mxp=0;
  37. F(i,1,len) {
  38. int j=(!mxp?0:min(mxp+ex[mxp]-i,ex[mxp+mxp-i]));
  39. while (t[i+j]==t[i-j])
  40. j++;
  41. ex[i]=j;
  42. if (i+ex[i]-1>mxp+ex[mxp]-1)
  43. mxp=i;
  44. }
  45. F(i,1,len)
  46. if (t[i]=='+')
  47. ans+=(ex[i]>>1);
  48. printf("%lld\n",ans);
  49. return 0;
  50. }
  51. int rd(void) {
  52. int x=0,f=1; char c=getchar();
  53. while (!isdigit(c)) {
  54. if (c=='-') f=-1;
  55. c=getchar();
  56. }
  57. while (isdigit(c)) {
  58. x=x*10+c-'0';
  59. c=getchar();
  60. }
  61. return x*f;
  62. }

【bzoj2085】Hamsters - KMP+倍增Floyd

题意

给定个长度总和不超过的字符串。
求一个最短的母串,使。 这n个字符串保证不互相包含。

分析

我们求出一个字符串后面接上另一个字符串需要增加的长度
直接通过KMP,在的暴力下求解。

然后相当于这样一个模型:
给定一个有向图,从任意点出发走条边的路径长度的最小值。
倍增Floyd求解。

在实现上,首先是关于个长度总和为的字符串的存储方式及使用方式。
我的方法是存到一个大的字符串里,两个不同的字符串用+号分割开。如果要访问某个位置,我们设,那么访问第个位置就为

然后就是关于倍增Floyd。
其实自己YY出来就好了。
一般或者的问题都是通过倍增来实现的。

还有就是关于快速幂的非递归实现方法。

  1. void pwr(Data &ans,Data &bas,int m) {
  2. while (m>0) {
  3. if (m%2==1)
  4. ans=ans+bas;
  5. bas=bas+bas;
  6. m>>=1;
  7. }
  8. }

注意写void比较好,否则返回值比较大就GG了。

代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cctype>
  4. #include <cmath>
  5. #include <algorithm>
  6. using namespace std;
  7. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  8. #define LL long long
  9. int rd(void);
  10. const int N=256;
  11. const int L=131072;
  12. const int U=32;
  13. const LL MAX=(LL)1e16;
  14. int n,m;
  15. char s[L];
  16. int star[N],fin[N],len[N];
  17. int nx[L];
  18. int unit;
  19. struct Data {
  20. LL go[N][N];
  21. Data(void) {
  22. memset(go,0,sizeof go);
  23. }
  24. friend Data operator + (Data a,Data b) {
  25. Data c;
  26. F(i,1,n) F(j,1,n) {
  27. c.go[i][j]=MAX;
  28. F(k,1,n)
  29. c.go[i][j]=min(c.go[i][j],a.go[i][k]+b.go[k][j]);
  30. }
  31. return c;
  32. }
  33. LL Min_path(void) {
  34. LL ans=MAX;
  35. F(i,1,n)
  36. F(j,1,n)
  37. ans=min(ans,go[i][j]);
  38. return ans;
  39. }
  40. }bas,ans;
  41. int Match(int c,int d) {
  42. int j=0;
  43. int dc=star[c]-1;
  44. int dd=star[d]-1;
  45. F(i,1,len[c]) {
  46. while (j>0&&s[dc+i]!=s[dd+(j+1)])
  47. j=nx[dd+j];
  48. if (s[dc+i]==s[dd+(j+1)])
  49. j++;
  50. }
  51. int ret=len[d]-j;
  52. return ret;
  53. }
  54. void pwr(Data &ans,Data &bas,int m) {
  55. while (m>0) {
  56. if (m%2==1)
  57. ans=ans+bas;
  58. bas=bas+bas;
  59. m>>=1;
  60. }
  61. }
  62. int main(void) {
  63. #ifndef ONLINE_JUDGE
  64. freopen("a.in","r",stdin);
  65. freopen("a.out","w",stdout);
  66. #endif
  67. n=rd(),m=rd();
  68. F(i,1,n) {
  69. s[fin[i-1]+1]='+';
  70. star[i]=fin[i-1]+2;
  71. scanf("%s",s+star[i]);
  72. len[i]=strlen(s+star[i]);
  73. fin[i]=star[i]+len[i]-1;
  74. }
  75. s[fin[n]+1]='+';
  76. F(c,1,n) {
  77. int dc=star[c]-1;
  78. nx[dc+1]=0;
  79. int j=0;
  80. F(i,2,len[c]) {
  81. while (j>0&&s[dc+i]!=s[dc+(j+1)])
  82. j=nx[dc+j];
  83. if (s[dc+i]==s[dc+(j+1)])
  84. j++;
  85. nx[dc+i]=j;
  86. }
  87. }
  88. F(i,1,n) F(j,1,n)
  89. if (i==j)
  90. ans.go[i][j]=len[i];
  91. else ans.go[i][j]=MAX;
  92. m--;
  93. F(i,1,n) F(j,1,n)
  94. if (i==j)
  95. bas.go[i][j]=len[i]-nx[star[i]+len[i]-1];
  96. else bas.go[i][j]=Match(i,j);
  97. pwr(ans,bas,m);
  98. LL ret=ans.Min_path();
  99. printf("%lld\n",ret);
  100. return 0;
  101. }
  102. int rd(void) {
  103. int x=0,f=1; char c=getchar();
  104. while (!isdigit(c)) {
  105. if (c=='-') f=-1;
  106. c=getchar();
  107. }
  108. while (isdigit(c)) {
  109. x=x*10+c-'0';
  110. c=getchar();
  111. }
  112. return x*f;
  113. }

【bzoj2086】Block - 单调栈

题意

给出个正整数,再给出一个正整数,现在可以进行如下操作:每次选择一个大于的正整数,将减去,选择中的一个加上。经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数都不小于
总共给出次询问,每次询问给出的不同,你需要分别回答。

分析

先来一个不相关的问题:如果可以随便算数,任意次数,求最大值。
注意要流量是守恒的,所以总流量是,最长的就是

现在要求大于的才能流动。
那么,问题变成了:求最长的区间,满足平均数大于等于

我们把每个位置减去,得到,问题变为:求最长的区间,满足平均数大于0。即:求最长的区间,满足和大于0。


所以要求:

我们考虑枚举右端点,看左端点要满足什么条件。
对于,若,则优,一定不需要作为左端点。
所以从进行加点,我们假如把每个点抽象成二维平面上的点,我们得到了递减的一段折线。
这个用栈记录一下即可。

枚举右端点。
考虑过右端点做一条水平的线,取下面的最左的点更新答案。
最左的点的右边的点一定不能取了,因为已经可以取到(最左的点,右端点),若再往中间取没有任何意义。

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <algorithm>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. #define D(i,a,b) for (int i=(a);i>=(b);i--)
  7. #define LL long long
  8. int rd(void);
  9. const int N=1048576;
  10. int n,m;
  11. int a[N];
  12. int nk;
  13. LL s[N];
  14. int stk[N],top;
  15. void Solve(int nk,char out) {
  16. F(i,1,n)
  17. s[i]=a[i]-nk;
  18. F(i,1,n)
  19. s[i]+=s[i-1];
  20. while (top>0)
  21. stk[top--]=0;
  22. F(i,0,n-1)
  23. if (!top||s[stk[top]]>s[i])
  24. stk[++top]=i;
  25. int ans=0;
  26. D(i,n,1) {
  27. while (top>0&&stk[top]>=i)
  28. stk[top--]=0;
  29. if (top>0&&s[i]>=s[stk[top]]) {
  30. while (top-1>0&&s[i]>=s[stk[top-1]])
  31. stk[top--]=0;
  32. ans=max(ans,i-stk[top]);
  33. }
  34. }
  35. printf("%d%c",ans,out);
  36. }
  37. int main(void) {
  38. #ifndef ONLINE_JUDGE
  39. freopen("a.in","r",stdin);
  40. freopen("a.out","w",stdout);
  41. #endif
  42. n=rd(),m=rd();
  43. F(i,1,n)
  44. a[i]=rd();
  45. F(i,1,m) {
  46. nk=rd();
  47. Solve(nk,(i==m)?'\n':' ');
  48. }
  49. return 0;
  50. }
  51. int rd(void) {
  52. int x=0,f=1; char c=getchar();
  53. while (!isdigit(c)) {
  54. if (c=='-') f=-1;
  55. c=getchar();
  56. }
  57. while (isdigit(c)) {
  58. x=x*10+c-'0';
  59. c=getchar();
  60. }
  61. return x*f;
  62. }

【bzoj2087】Sheep - 单调性+极角排序+区间DP

题意

给定个点,这些点组成一个凸包。
给定凸包内的个点,保证为偶数。
把凸包切成若干个三角形,求满足三角形的边上没有点且三角形内都有偶数个点的划分方案数,答案模


分析

安利一份题解:http://blog.csdn.net/zqh_wz/article/details/52734092

以凸包上的第一个点为基准点,对凸包的所有点进行极角排序。

表示把第个点到第个点划分成三角形的方案数。
则有:


其中,表示能否将连接一条边。
DP的复杂度为

如何判定能否连边?
有两个要求:①边上没有点 ②两边都有偶数个

对于从一个点出发,考虑它连向其他的所有点。
可以通过two pointer在实现连边。
具体见代码。

代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cctype>
  4. #include <algorithm>
  5. using namespace std;
  6. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  7. int rd(void);
  8. const int N=1024;
  9. const int K=32768;
  10. int n,nk,m;
  11. struct Point {
  12. int x,y;
  13. Point(int _x=0,int _y=0) {
  14. x=_x,y=_y;
  15. }
  16. friend Point operator - (Point a,Point b) {
  17. return Point(a.x-b.x,a.y-b.y);
  18. }
  19. void Read(void) {
  20. x=rd(),y=rd();
  21. }
  22. }x[N],p[K],org;
  23. int g[N][N];
  24. int f[N][N];
  25. int dot(Point a,Point b) {
  26. return a.x*b.y-a.y*b.x;
  27. }
  28. int cmp(Point a,Point b) {
  29. return dot(a-org,b-org)<0;
  30. }
  31. int DP(int l,int r) {
  32. int *res=&(f[l][r]);
  33. if (~*res)
  34. return *res;
  35. *res=0;
  36. F(i,l+1,r-1)
  37. if (g[l][i]&&g[r][i]){
  38. int tL=DP(l,i);
  39. int tR=DP(i,r);
  40. (*res+=(tL*tR)%m)%=m;
  41. }
  42. return *res;
  43. }
  44. int main(void) {
  45. #ifndef ONLINE_JUDGE
  46. freopen("a.in","r",stdin);
  47. freopen("a.out","w",stdout);
  48. #endif
  49. n=rd(),nk=rd(),m=rd();
  50. F(i,1,n)
  51. x[i].Read();
  52. F(i,1,nk)
  53. p[i].Read();
  54. org=x[1];
  55. sort(x+2,x+n+1,cmp);
  56. F(i,1,n) {
  57. org=x[i];
  58. sort(p+1,p+nk+1,cmp);
  59. int cnt=0;
  60. F(j,i+1,n) {
  61. while (cnt+1<=nk&&cmp(p[cnt+1],x[j]))
  62. cnt++;
  63. if (!(cnt&1)&&(cnt==nk||dot(p[cnt+1]-org,x[j]-org)>0))
  64. g[i][j]=g[j][i]=1;
  65. }
  66. }
  67. memset(f,-1,sizeof f);
  68. F(i,1,n-1)
  69. f[i][i+1]=1;
  70. int ans=DP(1,n);
  71. printf("%d\n",ans);
  72. return 0;
  73. }
  74. int rd(void) {
  75. int x=0,f=1; char c=getchar();
  76. while (!isdigit(c)) {
  77. if (c=='-') f=-1;
  78. c=getchar();
  79. }
  80. while (isdigit(c)) {
  81. x=x*10+c-'0';
  82. c=getchar();
  83. }
  84. return x*f;
  85. }

【bzoj2088】Teleportation - 分层图+构造

题意

给定一张图。
求给原图加最多的边,使得从起点到终点的最小步数不小于6。

分析

首先数据是合法的。

我们考虑最终的图长什么样。
总边数-减去最初的边数即可。

注意到最小的步数是个很小的常数,肯定有一些奇技淫巧。
最小步数想到了什么。。。
SAM?
分层图!
我们从开始,对图进行分层,那么层数为6。

起点在第1层。
与起点相邻的在第2层。
与起点相邻的点相邻的在第3层。
终点在第6层。
与终点相邻的在第5层。
与终点相邻的点相邻的在第4层。
还剩下一些点,我们需要考虑它怎么分配。

怎么分配要看怎么计算答案。
很明显,相同一层的可以相互连边,相邻两个不同层也可以两两连边。

设第一层的个数为a,第二层的个数为b...
以此类推,为 a b c d e f 个。
现在考虑增加个。

我们可以通过数值计算比较出两种方案的优劣。
由于是数值,数值具有传递性,所以方案的比较具有传递性。
我们考虑每增加一个,应该增加到哪里就行了。算增加一个的贡献。

增加到
增加到
增加到
增加到
增加到
增加到

所以肯定不会增加到
即:

代入:
增加到
增加到
增加到
增加到

所以只可能增加到或者
即比较的大小即可。

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <vector>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. #define LL long long
  7. int rd(void);
  8. const int N=65536;
  9. int n,m;
  10. int fs,ft;
  11. vector<int> g[N];
  12. int vis[N];
  13. int cnt[8];
  14. LL ans;
  15. int main(void) {
  16. #ifndef ONLINE_JUDGE
  17. freopen("a.in","r",stdin);
  18. freopen("a.out","w",stdout);
  19. #endif
  20. n=rd(),m=rd();
  21. fs=1,ft=2;
  22. F(i,1,m) {
  23. int x=rd(),y=rd();
  24. g[x].push_back(y);
  25. g[y].push_back(x);
  26. }
  27. vis[fs]=1;
  28. cnt[1]=1;
  29. vis[ft]=5;
  30. cnt[5]=1;
  31. F(i,1,g[fs].size()) {
  32. vis[g[fs][i-1]]=2;
  33. cnt[2]++;
  34. }
  35. ans+=(LL)cnt[2]*(cnt[2]-1)>>1;
  36. ans+=cnt[2];
  37. F(i,1,g[ft].size()) {
  38. vis[g[ft][i-1]]=4;
  39. cnt[4]++;
  40. }
  41. ans+=(LL)cnt[4]*(cnt[4]-1)>>1;
  42. ans+=cnt[4];
  43. cnt[3]=n-cnt[1]-cnt[2]-cnt[4]-cnt[5];
  44. ans+=(LL)cnt[3]*(cnt[3]-1)>>1;
  45. F(i,1,g[fs].size()) {
  46. int v=g[fs][i-1];
  47. F(j,1,g[v].size())
  48. if (!vis[g[v][j-1]]) {
  49. vis[g[v][j-1]]=3;
  50. ans+=cnt[2];
  51. }
  52. }
  53. F(i,1,g[ft].size()) {
  54. int v=g[ft][i-1];
  55. F(j,1,g[v].size())
  56. if (!vis[g[v][j-1]]) {
  57. vis[g[v][j-1]]=3;
  58. ans+=cnt[4];
  59. }
  60. }
  61. F(i,1,n)
  62. if (!vis[i]) {
  63. vis[i]=3;
  64. ans+=max(cnt[2],cnt[4]);
  65. }
  66. ans-=m;
  67. printf("%lld\n",ans);
  68. return 0;
  69. }
  70. int rd(void) {
  71. int x=0,f=1; char c=getchar();
  72. while (!isdigit(c)) {
  73. if (c=='-') f=-1;
  74. c=getchar();
  75. }
  76. while (isdigit(c)) {
  77. x=x*10+c-'0';
  78. c=getchar();
  79. }
  80. return x*f;
  81. }

【bzoj2089】Monotonicity - 贪心+dp

同【bzoj2090】


【bzoj2090】Monotonicity 2 - 贪心+dp

题意

给定一个长度为的序列,以及长度为的包含的字符串。
求最长的子序列,子序列的相邻项要满足大小关系。

分析

最初的想法是二维dp,但肯定不行。
这时候考虑优化的方法,可以证明


两棵权值树状数组实现。

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <algorithm>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. int rd(void);
  7. char rd_chr(void);
  8. const int N=1048576;
  9. const int A=1000000;
  10. int n;
  11. int a[N];
  12. int nk;
  13. char s[N];
  14. int f[N];
  15. int ans;
  16. int tr1[N]; //< 中 找比a[i]小的
  17. int eq[N]; //= 中 找等于a[i]的
  18. int tr2[N]; //> 中 找比a[i]大的
  19. int lowbit(int i) {
  20. return i&-i;
  21. }
  22. void Ins1(int x,int ad) {
  23. for (int i=x;i<=A;i+=lowbit(i))
  24. if (tr1[i]<ad)
  25. tr1[i]=ad;
  26. else break;
  27. }
  28. int Query1(int x) {
  29. int mx=0;
  30. for (int i=x;i>0;i-=lowbit(i))
  31. mx=max(mx,tr1[i]);
  32. return mx;
  33. }
  34. void Ins2(int x,int ad) {
  35. for (int i=x;i<=A;i+=lowbit(i))
  36. if (tr2[i]<ad)
  37. tr2[i]=ad;
  38. else break;
  39. }
  40. int Query2(int x) {
  41. int mx=0;
  42. for (int i=x;i>0;i-=lowbit(i))
  43. mx=max(mx,tr2[i]);
  44. return mx;
  45. }
  46. int main(void) {
  47. #ifndef ONLINE_JUDGE
  48. freopen("a.in","r",stdin);
  49. freopen("a.out","w",stdout);
  50. #endif
  51. n=rd(),nk=rd();
  52. F(i,1,n)
  53. a[i]=rd();
  54. F(i,1,nk)
  55. s[i]=rd_chr();
  56. F(i,nk+1,n)
  57. s[i]=s[i-nk];
  58. F(i,1,n) {
  59. int tL=Query1(a[i]-1);
  60. int tM=eq[a[i]];
  61. int tR=Query2(A-(a[i]+1)+1);
  62. int t=max(tL,max(tM,tR));
  63. f[i]=t+1;
  64. if (s[f[i]]=='<')
  65. Ins1(a[i],f[i]);
  66. else if (s[f[i]]=='=')
  67. eq[a[i]]=max(eq[a[i]],f[i]);
  68. else Ins2(A-a[i]+1,f[i]);
  69. }
  70. ans=*max_element(f,f+n+1);
  71. printf("%d\n",ans);
  72. return 0;
  73. }
  74. int rd(void) {
  75. int x=0,f=1; char c=getchar();
  76. while (!isdigit(c)) {
  77. if (c=='-') f=-1;
  78. c=getchar();
  79. }
  80. while (isdigit(c)) {
  81. x=x*10+c-'0';
  82. c=getchar();
  83. }
  84. return x*f;
  85. }
  86. char rd_chr(void) {
  87. char c=getchar();
  88. while (c!='<'&&c!='='&&c!='>')
  89. c=getchar();
  90. return c;
  91. }

【bzoj2091】The Minima Game - dp

题意

给出个正整数,A和B两个人轮流取数,A先取。每次可以取任意多个数,直到个数都被取走。
每次获得的得分为取的数中的最小值,A和B的策略都是尽可能使得自己的得分减去对手的得分更大。
在这样的情况下,最终A的得分减去B的得分为多少。

分析

按从小到大排序。
为了让自己的得分尽可能大,对手的得分尽可能小,每次必然取尾部的一段。
表示还剩前个没有取时,先手所能得到的最大值。

我开始的想法是:


从大到小处理,然后为答案。
这样是错的,因为当前决策的不一定是先手。
那求出来的是什么?
有可能是后手得分的最大值,或者先手的最大值。

那怎么办?
考虑从小到大处理
表示剩下最小的个,先手处理第个的最大值。


这样递推求出来的就是答案。

其实还有一种做法。
我们照样可以从大到小地推。
就是记分别表示到第个,当前是先手决策还是后手决策。
奠基:


转移:


答案:

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <algorithm>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. #define D(i,a,b) for (int i=(a);i>=(b);i--)
  7. #define LL long long
  8. int rd(void);
  9. const int N=1048576;
  10. int n,a[N];
  11. LL max_val,f[N];
  12. int main(void) {
  13. #ifndef ONLINE_JUDGE
  14. freopen("a.in","r",stdin);
  15. freopen("a.out","w",stdout);
  16. #endif
  17. n=rd();
  18. F(i,1,n)
  19. a[i]=rd();
  20. sort(a+1,a+n+1);
  21. F(i,1,n) {
  22. max_val=max(max_val,a[i]-f[i-1]);
  23. f[i]=max_val;
  24. }
  25. printf("%lld\n",f[n]);
  26. return 0;
  27. }
  28. int rd(void) {
  29. int x=0,f=1; char c=getchar();
  30. while (!isdigit(c)) {
  31. if (c=='-') f=-1;
  32. c=getchar();
  33. }
  34. while (isdigit(c)) {
  35. x=x*10+c-'0';
  36. c=getchar();
  37. }
  38. return x*f;
  39. }

【bzoj2092】Lamp

无题面。


【bzoj2093】Frog - 单调性+倍增

题意

给定个点,个点在一条直线上,给定河岸的距离。
对于每个点,它会跳到离它距离第近的点处。
求每个点跳步会跳到哪里。


分析

离一个点近的所有点集,是一个连续的区间。
直接求解必定TLE。
但是,这是个离线的问题,可以利用到一些连续性的东西。
由于一定,所以区间的长度相同。
随着的增大,点的连续区间在单调的后移。
所以我们维护two pointer即可。

然后求出每个点可以跳到哪里了。
这是个基环树。
可以考虑把环给求出来。
也可以考虑直接倍增求解。

因为倍增好写。
所以我写了倍增。
实现10s,我9s卡过了。

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <cmath>
  4. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  5. #define LL long long
  6. LL rd(void);
  7. const int N=1000001;
  8. const int U=60;
  9. int n,nk;
  10. LL m;
  11. LL a[N];
  12. int c;
  13. int nx[U][N];
  14. int Go(int x,LL m) {
  15. F(i,0,c)
  16. if (m>>i&1)
  17. x=nx[i][x];
  18. return x;
  19. }
  20. int main(void) {
  21. #ifndef ONLINE_JUDGE
  22. freopen("a.in","r",stdin);
  23. freopen("a.out","w",stdout);
  24. #endif
  25. n=rd(),nk=rd(),m=rd();
  26. F(i,1,n)
  27. a[i]=rd();
  28. c=(int)(log(m)/log(2));
  29. int l=1,r=nk+1;
  30. F(i,1,n) {
  31. while (l+1<=i&&r+1<=n) {
  32. if (a[i]-a[l]>a[r+1]-a[i])
  33. l++,r++;
  34. else break;
  35. }
  36. if (a[i]-a[l]>=a[r]-a[i])
  37. nx[0][i]=l;
  38. else nx[0][i]=r;
  39. }
  40. F(i,1,c)
  41. F(j,1,n)
  42. nx[i][j]=nx[i-1][nx[i-1][j]];
  43. F(i,1,n) {
  44. int t=Go(i,m);
  45. printf("%d",t);
  46. if (i<n)
  47. printf(" ");
  48. else printf("\n");
  49. }
  50. return 0;
  51. }
  52. LL rd(void) {
  53. LL x=0,f=1; char c=getchar();
  54. while (!isdigit(c)) {
  55. if (c=='-') f=-1;
  56. c=getchar();
  57. }
  58. while (isdigit(c)) {
  59. x=x*10+c-'0';
  60. c=getchar();
  61. }
  62. return x*f;
  63. }

【bzoj2095】Bridge - 二分答案+混合图欧拉回路

题意

给出一个个点条边的无向图,每个边各有一个权值。
现要从点出发,对每条边经过且仅经过一次。
求一种方案使经过的最大权值最小。
在bzoj上,输出这个权值即可。

分析

二分+混合图欧拉回路。

混合图欧拉回路的求解:

首先要判定基图连通,且每个点的度数为偶数。

然后把无向边任意定向,计算每个点入度与出度的差,按照定的方向连一条容量为1的边。在后面进行网络流的时候,若这条边流满,说明这条边要反向。

,则将源点与当前点连边,反之该点与汇点连边。容量应该是多少呢?流意味着中和,中和到d值为0。我们改变一条边,差值为2,所以容量应该是

那么,混合图欧拉回路如果有解,意味着满流。

代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cctype>
  4. #include <algorithm>
  5. using namespace std;
  6. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  7. #define fore(k,u) for (int k=hd[u];k>0;k=mp[k].nx)
  8. int rd(void);
  9. const int N=1024;
  10. const int M=2048;
  11. const int E=8192;
  12. const int INF=(0x7fffffff)>>1;
  13. const int REV_INF=-INF;
  14. int n,m;
  15. struct Ed {
  16. int a,b,c,d;
  17. Ed(int _a=0,int _b=0,int _c=0,int _d=0) {
  18. a=_a,b=_b,c=_c,d=_d;
  19. }
  20. }ed[M];
  21. int al,ar;
  22. int am;
  23. int f[N];
  24. int sd[N];
  25. int flow_siz;
  26. int fs,ft,siz;
  27. struct G {
  28. int v,f; int nx;
  29. G(int _v=0,int _f=0,int _nx=0) {
  30. v=_v,f=_f;
  31. nx=_nx;
  32. }
  33. }mp[E];
  34. int tot,hd[N];
  35. int q[N],qh,qt,lev[N];
  36. int mx_Flow;
  37. void Ins(int u,int v,int f) {
  38. int x=++tot;
  39. mp[x]=G(v,f,hd[u]);
  40. hd[u]=x;
  41. x=++tot;
  42. mp[x]=G(u,0,hd[v]);
  43. hd[v]=x;
  44. }
  45. int BFS(int fs,int ft) {
  46. memset(q,0,sizeof q);
  47. qh=qt=0;
  48. memset(lev,0,sizeof lev);
  49. q[++qt]=fs;
  50. lev[fs]=1;
  51. while (qh!=qt) {
  52. int x=q[++qh];
  53. fore(k,x)
  54. if (mp[k].f>0&&!lev[mp[k].v]) {
  55. lev[mp[k].v]=lev[x]+1;
  56. if (mp[k].v==ft)
  57. return 1;
  58. q[++qt]=mp[k].v;
  59. }
  60. }
  61. return 0;
  62. }
  63. int DFS(int x,int ft,int flow) {
  64. if (x==ft)
  65. return flow;
  66. int sum=0;
  67. fore(k,x)
  68. if (mp[k].f>0&&lev[mp[k].v]==lev[x]+1) {
  69. int t=DFS(mp[k].v,ft,min(flow,mp[k].f));
  70. if (t>0) {
  71. sum+=t;
  72. flow-=t;
  73. mp[k].f-=t;
  74. mp[k^1].f+=t;
  75. if (!flow) break;
  76. }
  77. else lev[mp[k].v]=INF;
  78. }
  79. return sum;
  80. }
  81. int Check(int lim) {
  82. memset(sd,0,sizeof sd);
  83. flow_siz=0;
  84. fs=ft=siz=0;
  85. tot=0;
  86. memset(hd,0,sizeof hd);
  87. siz=n;
  88. fs=++siz;
  89. ft=++siz;
  90. tot=1;
  91. F(i,1,m) {
  92. int a=ed[i].a,b=ed[i].b,c=ed[i].c,d=ed[i].d;
  93. if (c<=lim&&d<=lim) {
  94. sd[a]++;
  95. sd[b]--;
  96. Ins(a,b,1);
  97. }
  98. else if (c<=lim) {
  99. sd[a]++;
  100. sd[b]--;
  101. }
  102. else if (d<=lim) {
  103. sd[b]++;
  104. sd[a]--;
  105. }
  106. }
  107. F(i,1,n)
  108. if (sd[i]>0) {
  109. Ins(fs,i,sd[i]>>1);
  110. flow_siz+=(sd[i]>>1);
  111. }
  112. else if (sd[i]<0)
  113. Ins(i,ft,(-sd[i])>>1);
  114. mx_Flow=0;
  115. while (1) {
  116. int tag=BFS(fs,ft);
  117. if (!tag)
  118. break;
  119. int del=DFS(fs,ft,INF);
  120. mx_Flow+=del;
  121. }
  122. return mx_Flow==flow_siz;
  123. }
  124. int Find(int x) {
  125. if (f[x]==x)
  126. return x;
  127. else return f[x]=Find(f[x]);
  128. }
  129. int main(void) {
  130. #ifndef ONLINE_JUDGE
  131. freopen("a.in","r",stdin);
  132. freopen("a.out","w",stdout);
  133. #endif
  134. n=rd(),m=rd();
  135. F(i,1,m) {
  136. int a=rd(),b=rd(),c=rd(),d=rd();
  137. ed[i]=Ed(a,b,c,d);
  138. }
  139. F(i,1,n)
  140. f[i]=i;
  141. F(i,1,m) {
  142. int a=ed[i].a,b=ed[i].b;
  143. f[Find(a)]=Find(b);
  144. }
  145. F(i,2,n)
  146. if (Find(i)!=Find(1)) {
  147. printf("NIE\n");
  148. return 0;
  149. }
  150. F(i,1,m)
  151. sd[ed[i].a]++,sd[ed[i].b]++;
  152. F(i,1,n)
  153. if (sd[i]%2==1) {
  154. printf("NIE\n");
  155. return 0;
  156. }
  157. al=REV_INF;
  158. ar=REV_INF;
  159. F(i,1,m) {
  160. int c=ed[i].c,d=ed[i].d;
  161. al=max(al,min(c,d));
  162. ar=max(ar,max(c,d));
  163. }
  164. if (!Check(ar)) {
  165. printf("NIE\n");
  166. return 0;
  167. }
  168. while (al<ar) {
  169. am=(al+ar)>>1;
  170. int tag=Check(am);
  171. if (tag)
  172. ar=am;
  173. else al=am+1;
  174. }
  175. printf("%d\n",al);
  176. return 0;
  177. }
  178. int rd(void) {
  179. int x=0,f=1; char c=getchar();
  180. while (!isdigit(c)) {
  181. if (c=='-') f=-1;
  182. c=getchar();
  183. }
  184. while (isdigit(c)) {
  185. x=x*10+c-'0';
  186. c=getchar();
  187. }
  188. return x*f;
  189. }

【bzoj2096】Pilots - 单调队列

题意

长度为的给定的序列
找到一个最长的子串,满足任意两个难度差不会超过他设定的最大值。
输出最长子串的长度。

分析

我们考虑枚举右端点
为符合条件的最小左端点。
随着的增大,必然也不下降。

而且,能作为以为右端点的区间的最大值以及最小值。
随着的增大,是可以使用单调队列维护的。

所以我们维护两个单调队列就好了。
具体见实现。

从hzwer的博客处。
我涨姿势了。
假如单调队列不会写,可以用priority_queue实现单调队列。

代码

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <algorithm>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. int rd(void);
  7. const int N=3000010;
  8. int n,nk;
  9. int a[N];
  10. int q1[N],h1,t1;
  11. int q2[N],h2,t2;
  12. int l;
  13. int ans;
  14. int main(void) {
  15. #ifndef ONLINE_JUDGE
  16. freopen("a.in","r",stdin);
  17. freopen("a.out","w",stdout);
  18. #endif
  19. nk=rd(),n=rd();
  20. F(i,1,n)
  21. a[i]=rd();
  22. h1=h2=1;
  23. l=1;
  24. F(r,1,n) {
  25. while (h1<=t1&&a[r]>=a[q1[t1]])
  26. q1[t1--]=0;
  27. q1[++t1]=r;
  28. while (h2<=t2&&a[r]<=a[q2[t2]])
  29. q2[t2--]=0;
  30. q2[++t2]=r;
  31. while (a[q1[h1]]-a[q2[h2]]>nk)
  32. if (q1[h1]<q2[h2]) {
  33. l=q1[h1]+1;
  34. q1[h1++]=0;
  35. }
  36. else {
  37. l=q2[h2]+1;
  38. q2[h2++]=0;
  39. }
  40. ans=max(ans,r-l+1);
  41. }
  42. printf("%d\n",ans);
  43. return 0;
  44. }
  45. int rd(void) {
  46. int x=0,f=1; char c=getchar();
  47. while (!isdigit(c)) {
  48. if (c=='-') f=-1;
  49. c=getchar();
  50. }
  51. while (isdigit(c)) {
  52. x=x*10+c-'0';
  53. c=getchar();
  54. }
  55. return x*f;
  56. }

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注