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
所以就可以列出一个递推式子:
然后就再也没有过过样例。。。(就自闭了)
因为这个递推式是错的,它只考虑了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
于是我们开始反向推导:
发现
于是
我也就只能推到这里了。。。
接下来是官方题解
。。。