csp-s模拟7

这次状态不是很好,冲着T1磕了4个小时,后仨题看都没看。。。

A. median

去他丫的容斥,考虑排序,一个数作为中位数的方案数就是他左边有俩不同类型的数和右面有俩不同类型的数的总和

枚举哪些类型左边哪些右边,对每一位计算贡献就可以了,要提前预处理出来个数。

(有没有好心人看看我代码哪多乘了个4,最后答案我乘上4的逆元就过了,它没有道理但我拍不出来)

点击查看代码
#include<bits/stdc++.h>
#define int long long
const int mod=998244353;
const int maxn=1e5+10;
using namespace std;
int n,x,cnt,num[6][maxn<<3],ans;
struct lsx
{
	int x;
	char A;
	bool operator < (const lsx &a) const
	{
		return x<a.x;
	}
}a[maxn<<3];

int qpow(int x,int y)
{
	int ans=1;
	while(y)
	{
		if(y&1)ans=ans*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ans;
}

void pre()
{
	for(int i=1;i<=cnt;i++)
	{
		for(int j=1;j<=5;j++) num[j][i]=num[j][i-1];
		if(a[i].A=='A') num[1][i]++;
		if(a[i].A=='B') num[2][i]++;
		if(a[i].A=='C') num[3][i]++;
		if(a[i].A=='D') num[4][i]++;
		if(a[i].A=='E') num[5][i]++;
	}
}

signed main()
{
	freopen("median.in","r",stdin);
	freopen("median.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>x,a[++cnt]={x,'A'};
	for(int i=1;i<=n;i++) cin>>x,a[++cnt]={x,'B'};
	for(int i=1;i<=n;i++) cin>>x,a[++cnt]={x,'C'};
	for(int i=1;i<=n;i++) cin>>x,a[++cnt]={x,'D'};
	for(int i=1;i<=n;i++) cin>>x,a[++cnt]={x,'E'};
	sort(a+1,a+1+cnt);
	pre();
	for(int i=3;i<=cnt;i++)
	{
		for(int s=1;s<=5;s++)
		{
			if(s==a[i].A-'A'+1) continue;
			for(int k=1;k<=5;k++)
			{
				if(k==s||k==a[i].A-'A'+1) continue;
				for(int t=1;t<=5;t++)
				{
					if(t==s||t==k||t==a[i].A-'A'+1) continue;
					int h=15-s-k-t-(a[i].A-'A'+1);
					ans=(ans+a[i].x*num[s][i]%mod*num[k][i]%mod*(num[t][cnt]-num[t][i])%mod*(num[h][cnt]-num[h][i])%mod)%mod;
				}
			}
		}
	}

	cout<<ans*qpow(4,mod-2)%mod;
	
	return 0;
}
/*

*/

B. travel

我们知道对于一棵树来说,当 \(k\) 足够大时,答案就变成0,所以推广一下可知,题目就是在让我们找环,由于没有避免自环

所以考虑自环的贡献,如果只有一个自环的话,当 \(k\) 无限大时,答案只能是1,所以至少要有两个自环且联通才可以保证

有贡献,找这两种环就好了。

(我找这俩环写一个搜里了,确实对了,但一个点T了,卡常卡过去的,不理解你们为啥这么快)

点击查看代码
#include<bits/stdc++.h>
const int maxn=5e5+10;
using namespace std;
int n,m,head[maxn],nxt[maxn],to[maxn],tot,flag,cnt;
unordered_map<int,bool>vis[maxn],zi,v,s;

inline void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}

template<typename T> inline T read() {
  T X = 0; bool flag = 1; char ch = getchar();
  while (ch < '0' || ch > '9') {if (ch == '-') flag = 0; ch = getchar();}
  while (ch >= '0' && ch <= '9') {X = (X << 1) + (X << 3) + ch - '0'; ch = getchar();}
  if (flag) return X;
  return ~ (X - 1);
}

inline void lsx(int x)
{
	cnt++;
	if(cnt>n+m) return ;
	if(flag) return ;
	v[x]=1;
	s[x]=zi[x];
	for(int i=head[x];i;i=nxt[i])
	{
		if(flag) return; 
		int y=to[i];
		if(v[y])
		{
			flag=1;
			return ;
		}
		s[x]=zi[x];
		lsx(y);
		if(cnt>n+m) return ;
		if(flag) return; 
		if(s[x]&&s[y]) 
		{
			flag=1;
			return ;
		}
		s[x]|=s[y];
	}
	if(cnt>n+m) return ;
	if(!flag)v[x]=0; 
}

int main()
{ 
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
//	freopen("ex_data4.in","r",stdin); 
	ios::sync_with_stdio(0);
//	cin.tie(0),cout.tie(0);
	n=read<int>(),m=read<int>();
	for(register int i=1,x,y;i<=m;i++)
	{
		x=read<int>(),y=read<int>();
		if(x==y)
		{
			zi[x]+=1;
			continue; 
		}
		if(vis[x][y])continue;
		vis[x][y]=1;
		add(x,y);
	}
	for(register int i=1;i<=n;i++)
	{
		if(flag) break; 
		if(!v[i])lsx(i);
	}
	if(flag) puts("Yes");
	else puts("No");

	return 0;
}
/*

*/

C. game

结论:\(n\) 为偶数且 \(a\) 能两两相同配对则后手赢,否则先手必胜。

充分性很好证明,只需要后手模仿先手的操作,最后一定后手赢。

证明其必要性,当 \(a\) 不能两两配对时,先对其排序:

\(n\) 为奇数,则先手可以找到最大的一个不能匹配的去填补剩下的小的不能匹配的较小的那个,由于排过序,相邻作差

的和一定小于最大的那个数,所以情况成立

\(n\) 为偶数,则只需要将较大的一个堆中的一部分填补到较小的堆中,一定有一种取法满足条件,情况成立

(代码很容易被卡,但不想计数了,直接异或的)

点击查看代码
#include<bits/stdc++.h>
const int maxn=1e5+10;
using namespace std;
int n,temp,t;

int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--)
	{
		int x;
		cin>>n;
		temp=0;
		for(int i=1;i<=n;i++)
		{
			cin>>x;
			temp^=x;
		}
		if(n%2==0&&temp==0) puts("No");
		else puts("Yes");
	}

	return 0;
}
/*

*/
posted @ 2024-09-30 21:42  _君の名は  阅读(27)  评论(1编辑  收藏  举报