题目大意
给出n组数,分别放入k个集合,一个合法条件p(>=p 即合法)
每组数包含属于哪个集合以及它是否符合条件
每个集合内的任意两个数所在的位置之间,有一个点合法,则为一种方案
求方案总数
题目解析
f[i]表示 1~i-1 个点有多少个点可以和第 i 个点组成一种方案
从头到尾扫描数组,遇到两个数(k,i)同属一个集合(k,i 在集合内相邻),则判断k~i中是否有数合法
(判断是否合法用了前缀和,判断 k 和 i 两个数的前缀和的差是否大于0)
若有数合法,则 f[i]=num[a[i]],因为k,i在集合内相邻,若k,i可组成方案,则该集合内k之前的数都与i可组成方案
(num[a[i]]表示到 1~i-1中 在第a[i]个集合的个数)
若无数合法,则 f[i]=f[s[a[i]]],因为1~k-1有x个数组成方案,则这x个数都可与后面的i组成方案
(s[a[i]]表示集合a[i]在i之前的一个数,即上文的k)
f[1~n]的和即为所求
代码
#include<bits/stdc++.h>
#define N 200005
using namespace std;
int n,k,p,ans;
int a[N],b[N],flag[N],f[N],s[55],num[55];
int read()
{
char c=getchar();
int x=0;
while(!isdigit(c)) c=getchar();
while(isdigit(c))
{
x=x*10+c-48;
c=getchar();
}
return x;
}//快读
int main()
{
n=read();k=read();p=read();
for(int i=1;i<=n;i++)
{
a[i]=read();b[i]=read();
if(b[i]<=p)
flag[i]=1;//若该点合法,则记为1
}
for(int i=n;i>=1;i--)
flag[i]+=flag[i+1];//求第i个点以后有多少个点合法
for(int i=1;i<=n;i++)
{
//s[i]表示第a[i]个集合中,i的上一个数
if(s[a[i]]==0)//若i为该集合内的第一个数,则直接跳到下一次
{
s[a[i]]=i;
num[a[i]]++;
continue;
}
if(s[a[i]]!=i)
{
if(flag[s[a[i]]]>flag[i+1])//利用前缀和是否有差值判断合法
f[i]=num[a[i]];
else
f[i]=f[s[a[i]]];
s[a[i]]=i;//更新第a[i]个集合的数,即i
num[a[i]]++;//集合到目前为止的个数+1
}
}
for(int i=1;i<=n;i++)
ans+=f[i];
cout<<ans;
}