麻将

来源:第四届图灵杯趣味网络邀请赛
https://xjoi.net/contest/4228
https://contest.xinyoudui.com/statements/22a/b21510ada25e/statement_zh.html

有n种牌,编号从1到n,第i种牌有a[i]张。

给定常数x和y,问能否把这些牌分成若干组,每组满足下列条件之一:

刻子:包含x张编号相同的牌。 顺子:包含y张编号不同但连续的牌。

1<=n<=103,1<=x,y<=109

引理:如果能够成功分组,一定存在一种合法的分组方案使得编号最小的牌尽可能地组成刻子。

比如n=2x+1,考虑两种分法,分成2个刻子1个顺子 或 1个刻子x+1个顺子。

考虑每个顺子都是从编号1到y,x+1个顺子其实也就相当于编号1到y每个都组成一个刻子,然后总体是一个顺子。

所以所有方案其实都可以转化成刻子最多的那种方案。从前往后,重复子问题。

//模拟 O(N^2)
bool solve(){
	for(int i=1;i<=n-y+1;i++){
		a[i] %= x;
		for(int j=i+1;j<i+y;j++){
			if(a[j] < a[i]) return false;
			a[j] -= a[i];
		}
	}
	for(int i=n-y+2;i<=n;i++)
		if(a[i] % x) return false;
	return true;
}
int main(){
	cin>>n>>x>>y;
	for(int i=1;i<=n;i++) cin>>a[i];
	if(solve()) puts("Yes");
	else puts("No");
	return 0;
}

//差分优化 O(N) 一次可以直接修改整个范围
bool solve(){
	int tmp = 0;
	for(int i=1;i<=n-y+1;i++){
		tmp += b[i];//此时tmp即为a[i] 
		if(tmp < 0) return false;
		int r = tmp % x;//全做成刻子后剩下的 
		//相当于a[i]~a[i+y-1]每个数都减去了r 
		tmp -= r;
		b[i+y] += r;
	}
	for(int i=n-y+2;i<=n;i++){
		tmp += b[i];
		if(tmp < 0) return false;
		if(tmp % x) return false;
	}
	return true;
}
int main(){
	cin>>n>>x>>y;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i] = a[i] - a[i-1];
	}
	if(solve()) puts("Yes");
	else puts("No");
	return 0;
} 
posted @   Isaac233  阅读(176)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示