8.4考试总结(NOIP模拟30)[毛一琛·毛二琛·毛三琛]

最有名的莫过于想死一次吗。

前言

至今都不知道题目是个啥。。。

T1 毛一琛

解题思路

\(\mathcal{Meet\;In\;The\;Middle}\)

其实就是一个爆搜。。。

把整个区间分为两部分,每个部分有两个集合。

对于每一个数状态只有三种:集合1,集合2,不选。

然后对于已经两个区间内,如果每个区间中的两个集合价值之差相同的话,

这四个集合所组合成的两个集合一定可以分成两个值相同的集合。

然后就是对于了两半分别 DFS 就好了。。

  • 注意:全部开 long long 或者 map套 vector会 TLE。

code

#include<bits/stdc++.h>
//#define int long long
using namespace std;
int n,ans,cnt,s[30];
bool vis[1<<21];
vector<int> v[1<<21];
unordered_map<int,int> mp;
void dfs(int opt,int x,int anc,int sum1,int sum2,int sta)
{
	if(x==anc+1)
	{
		if(opt==1)
		{
			if(mp.find(abs(sum1-sum2))==mp.end())	return ;
			int tmp=mp.find(abs(sum1-sum2))->second;
			for(int i=0;i<v[tmp].size();i++)
				vis[sta<<n/2|v[tmp][i]]=true;
		}
		else
		{
			if(mp.find(abs(sum1-sum2))==mp.end())	mp[abs(sum1-sum2)]=++cnt;
			v[mp.find(abs(sum1-sum2))->second].push_back(sta);
		}
		return ;
	}
	dfs(opt,x+1,anc,sum1+((opt)?s[x+n/2]:s[x]),sum2,sta|(1<<x-1));
	dfs(opt,x+1,anc,sum1,sum2+((opt)?s[x+n/2]:s[x]),sta|(1<<x-1));
	dfs(opt,x+1,anc,sum1,sum2,sta);
}
signed main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)	scanf("%d",&s[i]);
	dfs(0,1,n/2,0,0,0);
	dfs(1,1,n-n/2,0,0,0);
	for(int i=1;i<(1<<n);i++)	ans+=vis[i];
	printf("%d",ans);
	return 0;
}

T2 毛二琛

解题思路

非常好的一个题(至少对于我来说是这样的)

首先发现对于 swap 的顺序是有一定优先级的。。

然后我们循环求出每一个 swap 操作的优先级。

接下来 DP 定义 f[i][j] 为前 i 个 swap 操作中 第 i 个优先级排名为 j

就可以简单的写出 \(\mathcal{O(n^3)}\) 的 DP转移。

发现可以前缀和优化,然后就可以把复杂度降低到 \(\mathcal{O(n^2)}\)

可以通过此题。

  • 注意:操作优先级以及 DP 的边界问题。

code

70pts

#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=5e3+10,mod=1e9+7;
int n,ans,f[N][N],s[N],opt[N];
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		s[i]=read()+1;
	for(int i=1;i<=n;i++)
	{
		if(s[i]-i>1)
		{
			if(!opt[i-2])	opt[i-2]=opt[i-3];
			for(int j=i-1;j<=s[i]+1;j++)
				opt[j]=opt[j-1]+1;
		}
		else
		{
			if(!opt[s[i]+1])	opt[s[i]+1]=opt[s[i]+2];
			for(int j=i-2;j>=s[i];j--)
				opt[j]=opt[j+1]+1;
		}
	}
	f[1][1]=1;
	for(int i=2;i<n;i++)
		for(int j=1;j<i;j++)
		{
			if(opt[i]>opt[i-1])
				for(int k=j+1;k<=i;k++)
					f[i][k]=(f[i][k]+f[i-1][j])%mod;
			if(opt[i]<opt[i-1])
				for(int k=1;k<=j;k++)
					f[i][k]=(f[i][k]+f[i-1][j])%mod;
			if(opt[i]==opt[i-1])
				for(int k=1;k<=i;k++)
					f[i][k]=(f[i][k]+f[i-1][j])%mod;
		}
	for(int i=1;i<n;i++)
		ans=(ans+f[n-1][i])%mod;
	printf("%lld",ans);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=5e3+10,mod=1e9+7;
int n,ans,f[N][N],pre[N],s[N],opt[N];
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		s[i]=read()+1;
	for(int i=1;i<=n;i++)
		if(s[i]-i>1)
		{
			if(!opt[i-2]&&i-3>=0)	opt[i-2]=opt[i-3];
			for(int j=i-1;j<=s[i]+1;j++)
				opt[j]=opt[j-1]+1;
		}
		else
		{
			if(!opt[i-1]&&i-2>=0)	opt[i-1]=opt[i-2];
			for(int j=i-2;j>=s[i];j--)
				opt[j]=opt[j+1]+1;
		}
	f[1][1]=pre[1]=1;
	for(int i=2;i<n;i++)
	{
		if(opt[i]>opt[i-1])
			for(int k=2;k<=i;k++)
				f[i][k]=(f[i][k]+pre[k-1])%mod;
		if(opt[i]<=opt[i-1])
			for(int k=1;k<i;k++)
				f[i][k]=(f[i][k]+pre[i-1]-pre[k-1]+mod)%mod;
		for(int j=1;j<=i;j++)
			pre[j]=(pre[j-1]+f[i][j])%mod;
	}
	for(int i=1;i<=n-1;i++)
		ans=(ans+f[n-1][i])%mod;
	printf("%lld",ans);
	return 0;
}

T3 毛三琛

解题思路

其实正解的时间复杂度是假的。

二分答案,查找每一个 x 下最小的最大值。

然后就是一些剪枝。。(不要开long long)

然后就没了。。(又水了一篇题解)(逃

code

#include<bits/stdc++.h>
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e4+10;
int n,m,ans=1e9,mod,sum,s[N],q[N];
bool check(int x)
{
	int tot=1,res=0;
	for(int i=1;i<=n;i++)
	{
		if(q[i]>x)	return false;
		if(res+q[i]<=x)	res+=q[i];
		else	res=q[i],tot++;
		if(tot>m)	return false;
	}
	return tot<=m;
}
signed main()
{
	n=read();
	mod=read();
	m=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	for(int x=0;x<mod;x++)
	{
		sum=0;
		for(int i=1;i<=n;i++)
		{
			q[i]=(x+s[i])%mod;
			sum+=q[i];
		}
		if(!check(ans))	continue;
		int l=0,r=sum+1,tmp=-1;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(check(mid)){tmp=mid;r=mid-1;}
			else	l=mid+1;
		}
		if(tmp!=-1)	ans=min(ans,tmp);
	}
	printf("%d",ans);
	return 0;
}
posted @ 2021-08-04 21:23  Varuxn  阅读(42)  评论(0编辑  收藏  举报