2024 洛谷月赛(柒)

月赛

GGrun %%%

T1 在相思树下 I

签到题QWQ,找规律易得。证明未知

每次一定会删掉一半的数,所以第 \(i\) 次操作都会提供一个 \(1<<i-1\) 的贡献。

这个贡献就是下一次会往后跳多少个位置。

假如一开始确定留下的是第一个,那删偶数不会有影响,而删奇数需要往后跳。

code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
int t;
LL n,k;
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld",&n,&k)	;
		LL ans=1;
		for(int i=0;i<k;i++)
		{
			int x; scanf("%d",&x);
			if(x==1) ans+=(1ll<<i);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T2 01-string

一眼 dp,可以开 \(f[i][4]\) 表示完成前 \(i\) 位的代价,\(4\) 表示反转,一,零覆盖,不动四种操作,记录上一次的操作。

注意如果是 \(0,1\),虽然可能不需要操作,但是由于区间连续的原因,可能进行覆盖操作更优,所以也要更新。

代码在这,但是假了。

假code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 5e5+5;
int T;
int n,f[N][4];
char s[N],t[N];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		memset(f,0x3f,sizeof(f));
		scanf("%s%s",(s+1),(t+1));
		n=strlen(s+1); f[0][0]=0;
		for(int g=1;g<=3;g++)
		for(int i=1;i<=n;i++)
		{
			if(s[i]==t[i]) f[i][0]=min({f[i-1][0],f[i-1][1],f[i-1][2],f[i-1][3],f[i][0]});
			else f[i][3]=min({f[i-1][0]+1,f[i-1][1]+1,f[i-1][2]+1,f[i-1][3],f[i-1][3]});
			if(t[i]=='1') f[i][1]=min({f[i-1][0]+1,f[i-1][1],f[i-1][2]+1,f[i-1][3]+1,f[i-1][1]});
			if(t[i]=='0') f[i][2]=min({f[i-1][0]+1,f[i-1][1]+1,f[i-1][2],f[i-1][3]+1,f[i-1][2]});
		}
		printf("%d\n",min({f[n][0],f[n][1],f[n][2],f[n][3]}));
	}
	return 0;
}

假好像不那么明显,因为找了好久都没有找到反例。

我们进行的线性 dp,它不会考虑操作区间交叉的情况,也就是一个字符只会被操作一次。

而由于反转操作的存在,是有一些情况必须先进行反转然后再统一覆盖的,并且一定是先反转再覆盖。

因为如果先覆盖再反转,可以用反转后再覆盖乘相反的代替,这两种是等价的。

所以既然会出现两种操作同时存在,并且是一种反转一种覆盖,那么我们可以把反转单开出来记。

分讨情况暴增。

code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 5e5+5;
int T;
int n,f[N][3][2];
char s[N],t[N];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		memset(f,0x3f,sizeof(f));
		scanf("%s%s",(s+1),(t+1));
		n=strlen(s+1);
		f[0][2][0]=0;
		for(int i=1;i<=n;i++)
		{
			if(s[i]==t[i])
			{
				f[i][2][0]=min({f[i-1][2][0],f[i-1][0][0],f[i-1][1][0],f[i-1][2][1],f[i-1][0][1],f[i-1][1][1]});
				if(s[i]=='0')
					f[i][0][0]=min({f[i-1][2][0]+1,f[i-1][0][0],f[i-1][1][0]+1,f[i-1][2][1]+1,f[i-1][0][1],f[i-1][1][1]+1});
				else
					f[i][1][0]=min({f[i-1][2][0]+1,f[i-1][0][0]+1,f[i-1][1][0],f[i-1][2][1]+1,f[i-1][0][1]+1,f[i-1][1][1]});	
			}
			else
			{
				if(s[i]=='0')
					f[i][1][0]=min({f[i-1][2][0]+1,f[i-1][0][0]+1,f[i-1][1][0],f[i-1][2][1]+1,f[i-1][0][1]+1,f[i-1][1][1]});
				else
					f[i][0][0]=min({f[i-1][2][0]+1,f[i-1][0][0],f[i-1][1][0]+1,f[i-1][2][1]+1,f[i-1][0][1],f[i-1][1][1]+1});	
			}
			if(s[i]!=t[i])
			{
				f[i][2][1]=min({f[i-1][2][0]+1,f[i-1][0][0]+1,f[i-1][1][0]+1,f[i-1][2][1],f[i-1][0][1],f[i-1][1][1]});
				if(s[i]=='0')
					f[i][0][1]=min({f[i-1][2][0]+2,f[i-1][0][0]+1,f[i-1][1][0]+2,f[i-1][2][1]+1,f[i-1][0][1],f[i-1][1][1]+1});
				else
					f[i][1][1]=min({f[i-1][2][0]+2,f[i-1][0][0]+2,f[i-1][1][0]+1,f[i-1][2][1]+1,f[i-1][0][1]+1,f[i-1][1][1]});	
			}
			else
			{
				if(s[i]=='0')
					f[i][1][1]=min({f[i-1][2][0]+2,f[i-1][0][0]+2,f[i-1][1][0]+1,f[i-1][2][1]+1,f[i-1][0][1]+1,f[i-1][1][1]});
				else
					f[i][0][1]=min({f[i-1][2][0]+2,f[i-1][0][0]+1,f[i-1][1][0]+2,f[i-1][2][1]+1,f[i-1][0][1],f[i-1][1][1]+1});		
			}
		}
		printf("%d\n",min({f[n][0][0],f[n][0][1],f[n][1][1],f[n][1][0],f[n][2][0],f[n][2][1]}));
	}
	return 0;
}

T3 在相思树下 II

尝试用英文写未果

其实还算好想的一道题,我们用近似线段树的方式递归,然后统计限制就好了。

容易看出 \(max\)\(min\) 其实就是规定至少有多少个人比他小/大,

因此我们在统计的时候可以记录每个点到达这一高度的限制,由于我们可以随意安排人的位置,所以最终取这一棵子树的最小限制作为递归的结果。

最终我们的得到的是一个范围,即如果第 \(i\) 个人想到达 \(d\) 层所在区间应是 \([s[i].fi+1,num-s[i].se]\),把合法的修改成 \(1\),用差分数组维护区间修改。

code
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
const int N = 1e6+5,L = __lg(N)+5;
int n,m,num,c[N],f[L][N];
pair<int,int> s[N];
pair<int,int> dfs(int k,int l,int r,int d)
{
	if(l==r) return {0,0};
	int mid=l+r>>1;
	pair<int,int> sl=dfs(k<<1,l,mid,d-1),sr=dfs(k<<1|1,mid+1,r,d-1),ans={num,num};
	if(c[k]==1)
	{
		for(int i=l;i<=mid;i++) s[i].fi+=sr.fi+1;
		for(int i=mid+1;i<=r;i++) s[i].fi+=sl.fi+1;
	}
	else 
	{
		for(int i=l;i<=mid;i++) s[i].se+=sr.se+1;
		for(int i=mid+1;i<=r;i++) s[i].se+=sl.se+1;
	}
	for(int i=l;i<=r;i++)
	{
		ans.fi=min(s[i].fi,ans.fi); ans.se=min(ans.se,s[i].se);
		++f[d][s[i].fi+1]; --f[d][num-s[i].se+1];
	}
	return ans;																									
}
int main()
{
	scanf("%d%d",&n,&m); num=1<<n;
	for(int i=1;i<num;i++) scanf("%d",&c[i]);
	dfs(1,1,num,n);	f[0][0]=1;
	for(int i=0;i<=n;i++) for(int j=1;j<=num;j++) f[i][j]+=f[i][j-1];
	while(m--)
	{
		int x,y; scanf("%d%d",&x,&y);
		printf("%s\n",f[y-1][x]>0?"Yes":"No");
	}
	return 0;
}

T4 落月摇情

大力分讨,咕咕咕。。。

posted @ 2024-07-21 20:55  ppllxx_9G  阅读(14)  评论(0编辑  收藏  举报