首页 > 其他分享 >Codeforces Round 961 (Div. 2)

Codeforces Round 961 (Div. 2)

时间:2024-07-25 10:31:45浏览次数:17  
标签:cnt 961 int 状压 Codeforces 合法 答案 集合 Div

ABC没什么,除了B2还没补。

主要就是这个D题。

这题我基本上需要想到的都想到了,没想到的部分就是记录不合法答案而非直接记录正确答案。
其实这思路也有点能够被启发的地方,就是只有某个位置前k个位置是又至少一个数字存在与我们的选择集合里的,我们才能够统计答案。也就是说,如果统计的是不合法的答案有哪些,我们只需要关注这个位置\(i\)的前\(k\)个位置,\([i-k,i]\)这个区间内的字符,这个就是滑动窗口,直接计算就可以了。但是如果是求合法答案,则需要关注的是整个区间,包括\([1,i]\)中间的所有字符都需要被考虑。
而除此之外,还有更重要的就是,合法的答案集合,它的子集不一定合法,而不合法的答案的子集则是一定不合法。
事实上很容易发现,一个答案集合不合法,也只能是因为这个答案集合的选择会导致在原本的字符串上,有连续的\(k\)个字符的位置没有出现答案在集合中的字符,这是它唯一不合法的情况。而全集,除去不合法的情况,剩下的就一定都是合法的情况了。

听起来是非常简单的思路,但是其实这种反向的思路,如果不是刻意去想真的是想不到。。。反正我是这样的。
这题的特点就在于,不合法的集合,它的子集也是不合法的,而全集的大小十分的有限,我们统计合法情况和不合法的情况的结果是一样的。这个不合法的集合的包含性是非常重要的,而合法集合无法快速统计的一个原因就是正确集合之间并没有什么关系。

我感觉,当这个题目的集合总数被我发现不是特别大的时候,我就应该出现了统计不合法集合有哪些的思路了。而不合法的集合和合法集合完全互补就是反向的做法的肯定。而状压的题目则都是这样,只不过这题这个思路十分突出的原因就在于这个状压的是答案集合。而答案集合又只有两种状态,一种合法状态,一种不合法状态。对于比较常见的状压dp,状压的集合虽然不会大,但是一个集合并不是简单的合法和不合法的区分,这个状压的更多的是作为状态而非答案。

这题想要做出来最重要的还是想到统计不合法的集合,这个不合法的集合所拥有的性质比合法集合优秀了太多太多。
不过我做题的时候其实尝试过从结尾处的字符必选的情况思考,从而发现了其实合法的答案非常的没有顺序。从此其实能够发现统计合法答案不是一个特别好的思路,但是我没有想到这一层。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int a=0,b=1;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
	return a*b;
}
int n,k,c,cnt[19],f[(1<<19)],s[(1<<19)];
inline int Count(int x)
{
	int sum=0;
	for(int i=0;i<c;i++)
	{
		if((x>>i)&1)sum++;
	}
	return sum;
}
int main()
{
//	cout<<Count(2)<<endl;
	int T=read();
	while(T--)
	{
		n=read(),c=read(),k=read();
		for(int i=0;i<(1<<c);i++)
			f[i]=0;
		for(int i=0;i<=c;i++)cnt[i]=0;
		int now=0;
		for(int i=1;i<=k;i++)
		{
			char x;cin>>x;
			int a=x-'A';
			s[i]=a;
			cnt[a]++;
			if(cnt[a]==1)now+=(1<<a);
		}
		f[(1<<c)-1-now]=1;
		for(int i=k+1;i<=n;i++)
		{
			int last=i-k;
			cnt[s[last]]--;
			if(cnt[s[last]]==0)now-=(1<<s[last]);
			char x;
			cin>>x;
			s[i]=x-'A';
			cnt[s[i]]++;
			if(cnt[s[i]]==1)now+=(1<<s[i]);
			f[(1<<c)-1-now]=1;
		}
		f[(1<<c)-1-(1<<s[n])]=1;
		for(int j=(1<<c)-1;j>=0;j--)
		{
			if(f[j]==1)continue;
			for(int i=0;i<c;i++)
			{
				if((j>>i&1)==0)f[j]|=f[j+(1<<i)];
			}
		}
		int Min=0x3f3f3f3f;
		for(int i=0;i<(1<<c);i++)
		{
			if(f[i]==0)
			{
//				cout<<i<<' ';
				Min=min(Min,Count(i));
			}
		}
		cout<<Min<<endl;
	}
	return 0;
}

写题的过程中能够很明显的发现,其实合法答案的分布并没有很明显的规律,而相比之下不合法答案的分布规律有点太明显了。

最终得到的其实就是对于这种状压,假如这个状压的是和答案相关的集合,可以被分为两个互为补集的答案部分,可以两边都试试。。
好蠢的结论。

标签:cnt,961,int,状压,Codeforces,合法,答案,集合,Div
From: https://www.cnblogs.com/HLZZPawa/p/18322440

相关文章

  • Codeforces Round 961 (Div. 2)
    A.Diagonals----------------------------题解----------------------------------注意读题,题目中只有i+j相同的格子才是一个对角线,也就是说,(1,1)(2,2)(3,3)的那条大斜线不是个对角线,如图所示这是一个3*3的图中所有的对角线,那么我们只需要如图所示,从中间往两边依次放就可以,......
  • codeforces div_2 961 题解报告
    codeforcesdiv_2961题解报告比赛链接:https://codeforces.com/contest/1995A.Diagonals题目翻译给定一个边长为\(n\)的正方形,给定\(k\),要往正方形选\(k\)个点,\(x+y\)相同的点构成对角线,问至少要几条对角线才能装下\(k\)个点。时限1s,空间限制256MB\(1\len\le100,0\l......
  • Codeforces Round 961 (Div. 2)
    A.JoeyTakesMoney--------------------------题解------------------------------------选取x和y替换掉a[i],a[j],前提是两者乘积相同,最后要求和数组最大,其实很简单,我们只需要不对另x=1y=a[j]*a[x]就行,从左到右遍历整个数组队a[i]和a[i+1]进行此操作,就可以得到我们想要的值......
  • Codeforces Round 961 (Div. 2)
    题目链接:CodeforcesRound961(Div.2)总结:B1wa两发可惜,C出得有点小慢。A.Diagonalsfag:贪心Description:给定一个\(n*n\)的棋盘,给定\(k\)个棋子,每个格子只能放一个棋子,求将棋子全部放入棋盘,至少需要占几条对角线。Solution:求最少占用,显然贪心处理,从最长的对角线开始占用,对......
  • Codeforces Round 961 (Div. 2)
    题目链接:CodeforcesRound961(Div.2)总结:B1wa两发可惜,C出得有点小慢。A.Diagonalsfag:贪心Description:给定一个n∗nn*n......
  • Codeforces Round 961 (Div. 2)
    A#include<bits/stdc++.h>usingnamespacestd;inta[200];voidsolve(){intn,k;cin>>n>>k;a[1]=n;for(intj=n-1,i=2;i<=1+(n-1)*2;i+=2,j--){a[i]=a[i+1]=j;}if(!k){cout<<0<<"\n......
  • Codeforces Round 961 (Div. 2) 补题记录(A~D)
    上大分,赢!A考虑将每一条对角线上可以存放的砝码数量都记录一下,从大到小排序,然后直接暴力贪心选择。此时可以发现数量一定形如\(n,n-1,n-1,n-2,n-2,n-3,n-3,\ldots,1,1\)这样的形式。直接暴力减即可。时间复杂度为\(O(n)\)。#include<bits/stdc++.h>#defineintlonglong......
  • Codeforces 1987C
    codeforcesP1987C给定一个n长度的数组,每一步都要遍历整个数组。如果某个元素是末尾元素或是比其后一个元素大,则该元素减去1直到该元素为0,求解总步数,算法复杂度要求\(O(n)\)先给出暴力解法,复杂度\(O(n^2)\): intt=0; do{ for(inti=0;i<n-1;i++){ if(a[i]......
  • C. Divisor Chain
    原题链接题解任何数一定可以被二进制表示下最低位的一及以下的二次方数整除code#include<bits/stdc++.h>#definelllonglong#definelowbit(x)((x)&(-x))usingnamespacestd;voidsolve(){intn;cin>>n;intm=log2(n);intn1=1<<m;vector......
  • Codeforces Round 957 (Div. 3)复盘
    今天打了一下DIV3,专业转了就是不一样T1Onlypluses这道题主要就是理解乘法分配律,最多的绝对是两数相乘数最大的。T2AngryMonkey这道题一遍AC,虽然很简单还是值得鼓励,主要还是数学问题,用笔写下来找到数学规律之后做起来就很快T3GorillaandPermutation这道题还是很简单......