@AkamemakA
2022-07-21T12:06:10.000000Z
字数 1440
阅读 132
Atcoder
题解
题目链接:点我
给定对数和一个整数,第对数包含两个数。定义一个长度为的序列是好的,当且仅当满足:是数列的一段连续子序列,且对数中每一对数都至少有一个数出现在中。
定义函数,表示长度为的满足以上定义的数列的个数。
要求输出。
第一行输入,以后行,每行两个数。
3 5
1 3
1 4
2 5
0 1 3 2 1
下面是所有的好的数列:
1 2
1 2
2 1
5 9
1 5
1 7
5 6
5 8
2 6
0 0 1 2 4 4 3 2 1
我们可以对每一个数(或),添加一个属性:表示数出现在那些编号的数对中。例如,在样例1中,数出现在了第和第二对数中,则{}。因为会有多个数,所以我们用来存储。
容易想到,如果一段数列中,所有的数的中,如果包含了到,那么说明这段序列一定是好的。另外,不难想到,如果一段序列是好的,那么这段序列向外扩展出的序列也一定是好的。
因此,这道题的思路就像滑动窗口一般。先定下窗口左端,然后枚举窗口右端。若某时某刻,找到了一段好的序列,那么这之后的序列一定都是好的序列。
而对于答案统计,我们可以用差分来实现。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m;
int a[N],b[N];
int cnt[N],ans[N]; //cnt[j]表示在一段序列中,数i的v[i]中的元素j出现的个数
vector<int>v[N]; //v[i]
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i];
for(int i=1;i<=n;i++){
v[a[i]].push_back(i);
v[b[i]].push_back(i);
}
int cntn=n; //cntn表示在寻找好的序列时,剩余未出现的数对的个数
for(int i=1,j=1;i<=m;i++){ //i为左端点
while(j<=m&&cntn){ //将右端点j向右移
for(auto &c:v[j]){//枚举v[j]并标记
if(cnt[c]==0) cntn--; //若数对c没有出现过,则标记
cnt[c]++;
}
j++;
}
if(cntn) break; //cntn=0,说明找到了好的序列,则更新nas数组
//如果cntn!=0,那么说明右端点找到了最后都没有找到好的序列,那么左端点再向右移也不可能再找到了,就退出循环
ans[j-i]++;ans[m+1-i+1]--; //更新答案
for(auto &c:v[i]){
cnt[c]--;
if(cnt[c]==0)cntn++;
}//将左端点右移,并将之前统计过的数还原
}
for(int i=1;i<=m;i++){
ans[i]+=ans[i-1];
cout<<ans[i]<<" ";
}
return 0;
}
完结撒花~