Codeforces Round 1035 (Div. 2)

A

显然如果^1用处就是
1.可能让a变成a-1
2.如果异或的代价更少那有些时候可以用来替代+1
然后就没了

B

特殊情况是只有一条线
然后是如果最长的那根线比其他所有线加起来都长,那就无法覆盖可达到的所有的距离了,左边界Max*2-sum,右边界sum。
因为精度的原因可能会wa。

C

首先如果是n是奇数,由于偶数个相同的数字异或起来是0,而所有数字异或0等于它本身。我们只需要所有数字都填一样的就能够满足。那答案自然就是左边界。

然后我们考虑n=偶数的情况。
按位考虑,观察真值表。
0&0=0 0^0=0
0&1=0 0^1=1
1&0=0 1^0=1
1&1=1 1^1=0
可以发现,如果n=2,那必定没有答案。

那对于其他偶数呢?
我们考虑把偶数分为两部分,两部分都是偶数个相同数字。那异或那边的必定等于0,而与那边,则需要我们构造让他们等于0 。
简单考虑一下,对于左边界的数字L,比他大但是和他与起来等于0的数字最小是多少?
那就是某个\(2^i\)
所以如果右边界R要大于这个数字,那就有答案了,否则就无解。

D

好烦,这个D能赛时过一车的。。明明真的很难。

题目需要我们对于一个组合\(a\)考虑所有能够取到的排列,然后对每一个排列计算价值,然后累加起来,并把这个称作\(f(a)\),而我们要做的就是对于所有可能的\(a\),累加\(f(a)\),计算答案。

说实话,我看到这两个"所有可能"在一起的时候,我就已经炸了,完全没思路。

那如果按照题面的思路来,就是先考虑一下对于每个a有多少排列,然后计算对于每个排列的贡献,再去枚举所有a。

但是这已经不好做到了一个极点了。

我们考虑对于每个排列,有多少种可能的a能够产生它。可以发现,一个数x如果出现了,那么就有x种可能的a。所以说一个含0排列的贡献就是其中不是0的数的乘积。

那就这个很好计算了,我们现在只需要尝试去枚举所有排列就可以。

我们用\(f[i][j]\)表示前\(i\)个数字都已经考虑过是否出现过,前面还有\(j\)个数字需要填,但是没有预留空位的时候,所有方案的贡献的总和是多少。

现在考虑转移
假如我们保持还有\(j\)个空位,那就是三种情况。
1.我们当前的要出现,然后从这\(j+1\)个空里面选一个摸走。那么方案数是对于这\(j+1\)个需要取的都是一个方案数。\(f[i][j]+=f[i-1][j]*(j+1)*i\)

2.我们当前的要出现,但是我们这个位置a是0,于是我们让空位数量+1 。\(f[i][j]+=f[i-1][j-1]*i\)

3.我们当前的不要出现,那要么这里是0,要么就是依旧从前面挑一个摸走。\(f[i][j]+=f[i-1][j+1]*(j+1)+f[i-1][j]\)

总体代码就是下面的。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
	ll 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;
}
ll f[5001][5001];
int main()
{
	int T=read();
	while(T--)
	{
		ll n=read(),m=read();
        for(int i=1;i<=n;i++)
		{
			for(int j=0;j<=n;j++)
			{
				f[i][j]=0;
			}
		}
		f[0][0]=1;
		for(ll i=1;i<=n;i++)
		{
			for(ll j=0;j<=n;j++)
			{
				if(j!=0)f[i][j]=(f[i][j]+f[i-1][j-1]*(i)%m)%m;
				f[i][j]=(f[i][j]+f[i-1][j]*((j+1)*i+1)%m)%m;
				if(j!=n)f[i][j]=(f[i][j]+f[i-1][j+1]*(j+1)%m)%m;
				
			}
		}
		cout<<f[n][0]%m<<endl;
	}
	return 0;
}
posted @ 2025-07-06 20:53  Tracer_w  阅读(88)  评论(0)    收藏  举报