原题链接:
https://ac.nowcoder.com/acm/contest/58860/A
题意:
有n个段,第i段有ki个不同的物品,物品编号在1~m之间,求所有连续的段的物品不同的种类一共有多少种
数据范围:
1<=n<=2e5,1<=m<=100
思路:
记录连续的段的不同数的不同种类总数,当我们走到i的时候,假设已有x,y这两个数,那么我们再走到i+1段,如果i+1段里没有包含除了x,y以外的其他的数,那么说明这一段和上一段的种类相同。
那么我们对这个步数进行优化,每次走到下一个位置必须满足下一个位置中包含已经走过的前面的数中没有的数,那么m总共100个,对于每个起点最多跳100次,那么分别以1~n为起点跳100次,找到所有不同种类数,时间复杂度就是2e7,不会超时
那么我们需要对于每个起点i,找出所有的物品的下一个位置的下标,下标从小到大依次跳,每次跳都可以使得所有数的种类数不同,记录每组种类数
那么肯定会有重复的种类,那么我们把所有种类放进一个set里进行去重之后,就可以得到所有的不同的种类数的方案了
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
int n,m;
bool st[200005][105];//记录第i段j是否出现
int id[105];//记录i的下标
pii nex[200005][105];//记录第i段的下一个数的标和下一个数的值
set<__int128_t> mp;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int k;
cin>>k;
while(k--){
int op;
cin>>op;
st[i][op]=true;//op在第i段
}
}
for(int i=1;i<=m;i++){
id[i]=3e5;//边界值
}
for(int i=n;i>=1;i--){
for(int j=1;j<=m;j++){
if(st[i][j]) id[j]=i;//如果在第i段出现过,id[j]更新
nex[i][j]={id[j],j};//第i段的j的下一个位置坐标是id[j],值是j
}
}
for(int i=1;i<=n;i++){
sort(nex[i]+1,nex[i]+1+m);//根据第i段中的下标从小到大排序
int op;
__int128_t con=0;//记录不同的物品数
for(int j=1;j<=m;j++){
if(nex[i][j].first==3e5)break;//如果当前已经凑齐了就break
op=nex[i][j].first;//记录当前的坐标
while(nex[i][j].first==op&&j<=m&&nex[i][j].first!=3e5){//将当前的坐标里的数都加上
con+=((__int128_t)1)<<nex[i][j].second;
j++;
}
j--;
mp.insert(con);//加入方案数中
}
}
cout<<mp.size();
return 0;
}