版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33229466/article/details/82292096
前言
开学之后估计就没办法打比赛了。
开场做掉A和B之后,C写了个NTT结果被卡常,然后就一直卡到快比赛结束,这时突然发现第二个生成函数只用预处理到一半!在还有两分钟结束的时候成功卡过,怒涨了一波rating。。。
题解
C - Triangular Relationship
乱做。。。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=200005;
int n,k,t[N];
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) t[i%k]++;
LL ans=0;
for (int i=1;i<=n;i++)
if (i%k==0) ans+=(LL)t[0]*t[0];
else if (i%k*2==k) ans+=(LL)t[k/2]*t[k/2];
printf("%lld\n",ans);
return 0;
}
D - All Your Paths are Different Lengths
考虑除了1和n外,第i个点向第i+1个点连一条权值为0和一条权值为 的边。然后把L拆成二进制,对于每个1都向对应的点连一条边就好了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=105;
int L,n,m,bin[N],a[N];
struct data{int x,y,w;}ans[N];
void add(int x,int y,int w)
{
m++;
ans[m].x=x;ans[m].y=y;ans[m].w=w;
}
int main()
{
scanf("%d",&L);
n=20;
bin[0]=1;
for (int i=1;i<=20;i++) bin[i]=bin[i-1]*2;
for (int i=n-1;i>=2;i--) add(i,i+1,bin[n-i-1]),add(i,i+1,0);
int top=0,sum=0;
while (L>=262144) add(1,2,sum),sum+=262144,L-=262144;
while (L) a[++top]=L%2,L/=2;
for (int i=top;i>=1;i--)
{
if (!a[i]) continue;
add(1,n-i+1,sum);
sum+=bin[i-1];
}
printf("%d %d\n",n,m);
for (int i=1;i<=m;i++) printf("%d %d %d\n",ans[i].x,ans[i].y,ans[i].w);
return 0;
}
E - Stop. Otherwise…
考虑对每个询问分别求答案。设当前询问为t,现在把1到k分成若干份,若i和t-i都存在就把他们分到一份,否则就把i单独分为一份。对于有两个数的份,显然两个数至多选一个。然后这个可以用生成函数来算贡献。复杂度是
。
正解是直接枚举有存在两个数的份有多少份至少选了一个数,然后直接用组合数算贡献,就可以做到
了,真的是又快又短。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=2005;
const int MOD=998244353;
int n,k,a[N*3],rev[N*3],f[N][N*3],g[N][N*3],L,ny,w1[N*3],w2[N*3],ans[N];
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(LL)ans*x%MOD;
x=(LL)x*x%MOD;y>>=1;
}
return ans;
}
void NTT(int *a,int f)
{
for (int i=0;i<L;i++) if (i<rev[i]) std::swap(a[i],a[rev[i]]);
for (int i=1;i<L;i<<=1)
{
int wn=(f==1?w1[i]:w2[i]);
for (int j=0;j<L;j+=(i<<1))
{
int w=1;
for (int k=0;k<i;k++)
{
int u=a[j+k],v=(LL)a[j+k+i]*w%MOD;
a[j+k]=(u+v)%MOD;a[j+k+i]=(u+MOD-v)%MOD;
w=(LL)w*wn%MOD;
}
}
}
if (f==-1) for (int i=0;i<L;i++) a[i]=(LL)a[i]*ny%MOD;
}
void pre()
{
int lg=0;
for (L=1;L<=n*2;L<<=1,lg++);
for (int i=0;i<L;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
for (int i=1;i<L;i<<=1) w1[i]=ksm(3,(MOD-1)/i/2),w2[i]=ksm(3,MOD-1-(MOD-1)/i/2);
ny=ksm(L,MOD-2);
for (int i=0;i<=n;i++) f[1][i]=1,g[1][i]=2;
g[1][0]=f[0][0]=g[0][0]=1;
NTT(f[1],1);NTT(g[1],1);NTT(f[0],1);NTT(g[0],1);
for (int i=2;i<=k/2;i++)
{
for (int j=0;j<L;j++) f[i][j]=(LL)f[i-1][j]*f[1][j]%MOD,g[i][j]=(LL)g[i-1][j]*g[1][j]%MOD;
NTT(f[i],-1);NTT(g[i],-1);
for (int j=n+1;j<=n*2;j++) f[i][j]=g[i][j]=0;
NTT(f[i],1);NTT(g[i],1);
}
for (int i=k/2+1;i<=k;i++)
{
for (int j=0;j<L;j++) f[i][j]=(LL)f[i-1][j]*f[1][j]%MOD;
NTT(f[i],-1);
for (int j=n+1;j<=n*2;j++) f[i][j]=0;
NTT(f[i],1);
}
}
int main()
{
scanf("%d%d",&k,&n);
pre();
for (int i=2;i<=k+1;i++)
{
int s=0;
for (int j=1;j<=k;j++) if (j*2!=i&&j<i&&i-j<=k) s++;
for (int j=0;j<L;j++) a[j]=(LL)f[std::max(0,k-s-((i&1)?0:1))][j]*g[s/2][j]%MOD;
NTT(a,-1);
if (i&1) printf("%d\n",ans[i]=a[n]);
else printf("%d\n",ans[i]=(a[n]+a[n-1])%MOD);
}
for (int i=k+2;i<=k*2;i++) printf("%d\n",ans[k-(i-(k+2))]);
return 0;
}
F - Revenge of BBuBBBlesort!
考虑判断能否从初始排列顺着构造成给定排列。首先有个结论就是如果我们交换了对i两侧的数进行交换,那么肯定不会对i-1和i+1进行操作。那么显然一个数只能朝着一个方向走。如果答案合法那么我们一定可以将序列分成若干个区间[l,r],其中l,l+2,…,r都是一定要移动的,其他位置则不需要移动。首先这些数必须要构成一个l到r的排列,其次因为往同一个方向移动的两个数的相对位置必然不变,所以这个区间合法当且仅当往左移的数的目标位置单调递增,往右移的数同理。那么就可以 出解了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=300005;
int n,a[N],pos[N];
void fff()
{
puts("No");
exit(0);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (a[i]%2!=i%2) fff();
pos[a[i]]=i;
}
pos[0]=pos[n+1]=-1;
for (int i=1;i<=n;i++)
if (pos[i-1]!=i-1&&pos[i]!=i&&pos[i+1]!=i+1) fff();
int l=1;
while (l<=n)
{
while (l<=n&&pos[l]==l) l++;
if (l>n) break;
int r=l;
while (r+2<=n&&pos[r+2]!=r+2&&pos[r+1]==r+1) r+=2;
for (int i=l;i<=r;i+=2) if (pos[i]<l||pos[i]>r) fff();
int ls1=0,ls2=0;
for (int i=l;i<=r;i+=2)
if (pos[i]<i)
{
if (pos[i]<ls1) fff();
ls1=pos[i];
}
else
{
if (pos[i]<ls2) fff();
ls2=pos[i];
}
l=r+1;
}
puts("Yes");
return 0;
}