A列车售货员难题(河南省第十四届ICPC大学生程序设计竞赛)

该文章是关于解决一个算法问题的思路描述,给定n个段,每个段包含若干不同物品,目标是计算所有连续段中物品不同种类的总数。通过记录每个段的物品出现情况,找出每个段下一个位置的下标,并用集合去重以找到所有不同种类数的方案。最后,计算集合的大小得出答案。
摘要由CSDN通过智能技术生成

原题链接:
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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值