杂题

预计是以后感觉比较有价值并且不好明确分为某一类的题都放在里面

[ABC124D] Handstand

image
每次操作可以对一个子串反转,仔细想一下,不难想到每次应该都是对一个全为0的子串进行反转。因为假如我们将一个串进行操作使其变成全部为1,只对0操作的总操作数是一定是最低的。因为如果对1操作还需要额外多进行操作将1变回来,即使在0比1多的情况下也是如此(0串的数量最多只会比1串多1,在这种情况下操作0的数量和先操作全部再操作1的数量相同)。
然后我们明确我们反转每次只会对一个整体0串处理,这时就可以把所有串分割开并表示为数字的形式来计算他们的贡献。
比如1100这个串里,1的数量是2,0的数量也是2,他们对长度的贡献都相同,那么如何区分呢?我们将0串标记为负数即可。然后将他们各自的贡献存进一个数组里。比如110011110,存到数组里就是2 -2 4 -1.
接下来怎么做?我们知道我们最多有k次对0块进行操作的机会。那么我们就可以维护一个滑动窗口,时刻记录窗口中负数的数量令其不大于k。
然后进队时维护队列的总贡献即可。
坑点:一般来说负数是可以入队的,但当k=0时负数连队都不能入也不能计算贡献,因此需要特判,卡了我挺久

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int n,k;
int a[maxn],dp[maxn];
char c[maxn];
queue <int> q;
int main()
{
	cin>>n>>k;
	scanf("%s",c+1);
	c[0]='!';
	int s=1,cnt=0;
	for(int i=1;i<=n;i++)
	{
		if(c[i]!=c[i+1])
		{	
			if(c[i]=='0') a[++cnt]=-1*s;
			else a[++cnt]=s;
		    s=1;
		}
		else s++;
	}
	int sum=0,ans=0,nowsum=0;
	for(int i=1;i<=cnt;i++)
	{
		if(a[i]<0)
		{
			if(sum<k) 	{sum++;}
			else 
			{
				while(!q.empty()&&q.front()>0) nowsum-=q.front(),q.pop();
				if(!q.empty())
				{
				nowsum+=q.front();
				q.pop();
			    }
			}
		} 
		if(k!=0||a[i]>0) {q.push(a[i]);nowsum+=abs(a[i]);}
		ans=max(ans,nowsum);
	}
	cout<<ans;
	return 0;
}

CF edu 164 D

image
题目大意:给你一个序列,求出它的所有子序列的价值的和。
子序列的价值定义为:从子序列中的一个或两个位置各取一个1作为一组,令子序列清空的最小组数即为子序列的价值。例如:子序列若为1,2,3价值就是3,我们可以选择一次位置一和位置三为一组,两次位置二和位置三为两组。
思路:首先考虑对于一个子序列的价值该如何计算。我们要尽量让每一组都是两个这样可以最小化组数,通常情况下我们总能找到一种方案使得他们两两配对,最后最多剩1个,记子序列的和为s,这种情况下的价值为(s+1)/2.有一种例外是如果子序列中最大的数超过了子序列和的一半,那么始终会有一部分没法和其他匹配,此时子序列的价值是子序列中最大的数。
我们可以先将所有的情况的答案都加上(s+1)/2,对于特殊部分我们之后再进行处理。
我们将子序列为s的所有情况的数量都计算出来并令答案加上(s+1)/2,求序列和为s的数量可以通过01背包来解决,记dp[j]为和为j的子序列的数量,dp[j]=dp[j-a[i]]+dp[j].i为当前枚举到的编号。
然后我们从0到s枚举加上所有情况的答案
接着处理特殊情况,对于和为j的情况,当最大的数大于((j+1)/2)时答案更新为a[i],由于之前已经加上了一部分,因此我们要减去这部分再加上ai。我们可以考虑反过来思考,枚举每个ai,当序列其他部分小于ai时就是特殊情况,我们继续用背包解决
代码如下

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int a[5005];
int dp[25000005];
signed main()
{
	int n,sum=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i],sum+=a[i];
	dp[0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=sum;j>=a[i];j--)
		{
			dp[j]=dp[j]+dp[j-a[i]];
		}
	}
	int ans=0;
	for(int i=0;i<=sum;i++)//正好有i个小球有dp[i]组,答案为(i+1)/2 
	{
		ans=(ans+dp[i]*((i+1)/2)%mod)%mod;
	}
	//对于个数为i的小球,当一个组合剩下的球数<dp[i]时,答案为ai
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<a[i];j++)
		{
			ans=(ans+(dp[j]*(a[i]-(a[i]+j+1)/2)%mod))%mod;
		}
	} 
	cout<<ans<<endl;
	return 0;
}
posted @ 2024-04-11 18:27  miku今天吃什么  阅读(11)  评论(0)    收藏  举报