a

时限: 2 Sec  内存: 512 MB

题目描述

输入格式

输入样例

6
4 2 2 4 2 5

输出样例

6

提示

 

 

 

题解

想了30min

如果把i看成x坐标,si看成y坐标

发现就是快速维护在直线y=x-i下方的点的个数

如果直线向左移动一位

则直线下方的点会对答案造成-1的贡献,上方的点会对答案造成+1的贡献(两端的贡献当然要单独算)

但是有些点从下方会移动到上方,我们可以把直线下方的点按到直线的距离排序

于是就想到了用set

但是一看数据范围,应该是一个O(n)的做法

然后就发现可以直接用一个桶来存某个距离的点的个数

所有点的距离都统一为它到y=x的距离(其实就旋转了一下坐标)

就可以了

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
#define N 2000005
#define LL long long
int a[2*N],cnt[2*N];
inline int ab(int x){return x<0?-x:x;}
int main()
{
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	int n,i,ct=0;LL ans,sum=0;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
		a[i+n]=a[i];
		if(i-a[i]>0){cnt[i-a[i]]++;ct++;}
		sum+=1ll*ab(i-a[i]);
	}
	ans=sum;
	for(i=1;i<=n;i++){
		sum+=1ll*(n-ct)-1ll*ct;
		sum+=1ll*ab(n-a[i+n])-a[i+n];
		if(i+n-a[i+n]>i){cnt[i+n-a[i+n]]++;ct++;}
		ct-=cnt[i];
		ans=min(ans,sum);
	}
	printf("%lld",ans);
}

 

 

 

 

 

b

时限: 1 Sec  内存: 512 MB

题目描述

 

 

输入格式

输入样例

4
2 1 1 2

输出样例

2

提示

 

 

 

 

题解

我们可以发现一个所有点度数都为1或2的连通块,度数为1的点只有两个或0个

那么我们可以先算一下度数都为1的方案

自己手推一下(找规律),发现它的递推式是g[i]=g[i-1]*(2*i-1)____有i对1的方案

然后再看一下度数都为2的方案

有点像第一类斯特林,但是集合的大小要大于3

所以就可以列出一个递推式子:

f[i][j]=f[i-3][j-1]+f[i-1][j]*(i-1)

然后就再也没有过过样例。。。(就自闭了)

因为这个递推式是错的,它只考虑了i号点与i-1,i-2分为一个集合的情况

其实i号点可以跟前面任意两个点新建一个集合,i-1,i-2就被加入到f[i-3][j-1]中计算

所以f[i-3][j-1]要乘一个C(i-1,2)

 

然后怎么做呢?

枚举成环的2的个数,则剩下的2就是成链(这个可以隔板法再乘以排列)

即选出i个2(C(cnt2,i))乘以i个2成环的方案(f[i])乘以cnt2-i个2成链的方案((cnt2-i)!*C(cnt2-i-1+cnt1,cnt1-1))

由于链的首尾也有不同的排布方案,1也有不同的组合方式,所以还要乘一个g[cnt1]

注意两个1可以单独成链,所以成链方案是C(cnt2-i-1+cnt1,cnt1-1)而不是C(cnt2-i-1,cnt1-1)

(考试的时候想到了化学,然后降智破环为链去重容斥GG2333)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define N 2005
const int mod=998244353;
int f1[N],f2[N][N],fs2[N],a[N];
int inv[N],jc[N];
int C(int x,int y)
{
	return 1ll*jc[x]*inv[y]%mod*inv[x-y]%mod;
}
int main()
{
	//freopen("b.in","r",stdin);
	//freopen("b.out","w",stdout);
	f1[0]=1;jc[0]=jc[1]=1;inv[0]=inv[1]=1;
	for(int i=1;i<=2000;i++){
		f1[i]=1ll*f1[i-1]*(2*i-1)%mod;
		jc[i]=1ll*jc[i-1]*i%mod;
		if(i>=2) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=1;i<=2000;i++)inv[i]=1ll*inv[i]*inv[i-1]%mod;
	f2[0][0]=1;
	for(int i=0;i<=2000;i++){
		for(int j=0;j<=i;j++){
			if(!f2[i][j])continue;
			f2[i+3][j+1]=(1ll*f2[i+3][j+1]+1ll*f2[i][j]*C(i+2,2))%mod;// !!!!!!
			f2[i+1][j]=(1ll*f2[i+1][j]+1ll*i*f2[i][j])%mod;
			fs2[i]=(1ll*fs2[i]+1ll*f2[i][j])%mod;
		}
	}
	//printf("%d\n",f2[6][2]);
	int n,sum=0,cnt1=0,cnt2=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		if(a[i]==1)cnt1++;
		if(a[i]==2)cnt2++;
		sum+=a[i];
	}
	if(sum&1){printf("0");return 0;}
	if(sum==n){printf("%d",f1[sum/2]);return 0;}
	if(sum==2*n){printf("%d",fs2[n]);return 0;}
	int ans=0;cnt1>>=1;
	for(int i=0;i<=cnt2;i++)
		ans=(1ll*ans+1ll*C(cnt2,i)*fs2[i]%mod*jc[cnt2-i]%mod*C(cnt2-i+cnt1-1,cnt1-1)%mod*f1[cnt1])%mod;//  !!!!
	printf("%d",ans);
}

 

 

 

 

 

问题 C: c

时限: 1 Sec  内存: 128 MB

题目描述

输入格式

输出格式

输入样例

1
10 1 1
10 1 1
6 1 1

输出样例

8

提示

 

 

 

 

题解

莫比乌斯反反演2333

感觉出题人就是把一个式子用莫比乌斯反演推到一半,让我们推出原来式子2333

于是我们开始反向推导:

\sum_{d|gcd(n,s)}\sum_{g|\frac{n}{d}}\mu(g)*\left \lfloor \frac{m}{dg} \right \rfloor

=\sum_{d|gcd(n,s)}\sum_{g|\frac{n}{d}}\sum_{i=1}^{\left \lfloor \frac{m}{dg} \right \rfloor}\mu(g)

=\sum_{d|gcd(n,s)}\sum_{i=1}^{\left \lfloor \frac{m}{d} \right \rfloor}\sum_{g|gcd(\frac{n}{d},i)}\mu(g)

发现

\sum_{g|gcd(\frac{n}{d},i)}\mu(g)=[gcd(\frac{n}{d},i)=1]

于是

=\sum_{d|gcd(n,s)}\sum_{i=1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(\frac{n}{d},i)=1]

我也就只能推到这里了。。。

接下来是官方题解

。。。