ARC140 题解
A. Right String
题目描述
给定一个长为 \(n\) 的字符串 \(s\) ,字符集为全体小写字符。
你可以修改至多 \(k\) 个字符串,求 \(s\) 的循环节长度最小值。
数据范围
- \(1\le n\le 2000,0\le k\le n\) 。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{1024MB}\) 。
分析
枚举循环节长度 \(d\) ,判断是否可行。
将下标按\(\bmod d\) 分类,每一类保留出现字符最多的一种。
时间复杂度 \(O(n^2)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2005;
int k,n,res;
char s[maxn];
int cnt[maxn][26];
bool check(int x)
{
if(n%x) return false;
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++) cnt[i%x][s[i]-'a']++;
int sum=0;
for(int i=0;i<=x-1;i++)
{
int mx=0;
for(int j=0;j<=25;j++) mx=max(mx,cnt[i][j]);
sum+=mx;
}
return sum>=n-k;
}
int main()
{
scanf("%d%d%s",&n,&k,s+1);
for(int i=1;i<=n;i++)
if(check(i))
{
res=i;
break;
}
printf("%d",res);
return 0;
}
B. Shorten ARC
题目描述
给定一个长为 \(n\) 字符串 \(s\) ,字符集为 \(\texttt{A,R,C}\) 。
你可以进行如下操作:
- 在第奇数步将 \(\texttt{ARC}\) 替换成 \(\texttt R\) 。
- 在第偶数步将 \(\texttt{ARC}\) 替换成 \(\texttt{AC}\) 。
求操作步数最大值。
数据范围
- \(1\le n\le 2\cdot 10^5\) 。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{1024MB}\) 。
分析
显然每个 \(\texttt R\) 对答案的贡献独立,记它的权值为两侧配对的 \(\texttt A\) 和 \(\texttt C\) 的个数。
第奇数步会将某个 \(\texttt R\) 的权值减一,第偶数步会删去一个权值。
贪心,奇数步操作除 \(1\) 之外的最小权值,偶数步如果有 \(1\) 就删 \(1\) ,否则删除当前最小的权值,模拟即可。
时间复杂度 \(\mathcal O(n\log n)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,del,res;
char s[maxn];
priority_queue<int,vector<int>,greater<int>> q;
int main()
{
scanf("%d%s",&n,s+1);
for(int i=1;i<=n;i++)
{
if(s[i]!='R') continue;
int j=1;
while(s[i-j]=='A'&&s[i+j]=='C') j++;
if((--j)!=0) q.push(j);
}
while(!q.empty())
{
int u=q.top();
q.pop();
if(u==1)
{
del++;
continue;
}
if(res%2==0) res++,q.push(u-1);
else
{
res++;
if(del!=0) del--,q.push(u);
}
}
printf("%d",res+del);
return 0;
}
C. ABS Permutation (LIS ver.)
题目描述
定义一个排列 \(p\) 的快乐程度为 \(|p_i-p_{i+1}|\) 的最长严格上升子序列长度。
给定 \(p_1\) ,构造一个快乐程度最大的排列 \(p\) 。
数据范围
- \(2\le n\le 2\cdot 10^5\) 。
- \(1\le p_1\le n\) 。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{1024MB}\) 。
分析
如果没有 \(p_1\) 的限制,显然上界为 \(n-1\) 。
对于偶数(以\(10\)为例):
5 6 4 7 3 8 2 9 1 10
6 5 7 4 8 3 9 2 10 1
对于奇数(以\(11\)为例):
6 5 7 4 8 3 9 2 10 1 11
所以如果 \(p_1\) 刚好是 \(1\sim n\) 的中位数,那么直接像上面这样构造即可。
如果不是,容易证明无法达到 \(n-1\) 的上界。
但可以做到 \(n-2\) ,将除掉 \(p_1\) 以外的数仿照上面的“螺旋”型排列即可。
时间复杂度 \(\mathcal O(n)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;
int n,x;
int q[maxn];
void work(int n)
{
int k=(n+1)/2;
if(n%2==0) for(int i=1;i<=k;i++) q[2*i-1]=k-i+1,q[2*i]=k+i;
else
{
q[1]=k;
for(int i=1;i<=k;i++) q[2*i]=k-i,q[2*i+1]=k+i;
}
}
int main()
{
scanf("%d%d",&n,&x);
if(n%2==0)
{
if(x==n/2)
{
work(n);
for(int i=1;i<=n;i++) printf("%d ",q[i]);
exit(0);
}
if(x==n/2+1)
{
work(n);
for(int i=1;i<=n;i++) printf("%d ",n+1-q[i]);
exit(0);
}
work(n-1);
printf("%d ",x);
for(int i=1;i<=n-1;i++) printf("%d ",q[i]+(q[i]>=x));
}
else
{
if(x==(n+1)/2)
{
work(n);
for(int i=1;i<=n;i++) printf("%d ",q[i]);
exit(0);
}
work(n-1);
printf("%d ",x);
for(int i=1;i<=n-1;i++) printf("%d ",q[i]+(q[i]>=x));
}
return 0;
}
D. One to One
题目描述
无向图 \(G\) 有 \(n\) 条边(允许有重边和自环),第 \(i\) 条边连接 \((i,x_i)\) ,这张图的贡献是连通块个数。
给定部分 \(x_i\) ,求对剩下的 \(x_i\) 任意赋值,得到的所有 \(G\) 的贡献之和,对 \(998244353\) 取模。
数据范围
- \(1\le n\le 2000\) 。
- \(1\le x_i\le n\) 或 \(x_i=-1\) ,后者表示 \(x_i\) 没有确定。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{1024MB}\) 。
分析
给无向边定向,第 \(i\) 条边为 \(i\to x_i\) 。
由于每个点出度都为 \(1\) ,所以无向图连通块个数等于有向图连通块个数,进一步等于内向基环树的个数。
假设还有 \(cnt\) 条边没有确定,先把剩下的 \(n-cnt\) 条边连上,那么会形成一些连通块,并且每个连通块要么是树(\(x_{rt}=-1\)),要么是基环树。
对基环树计数还是不好做,但是注意到每个基环树恰有一个环,所以问题转化为对环计数。
一棵树向基环树连边不会产生贡献。
因为不会产生新的环。
每棵基环树的贡献是 \(n^{cnt}\) 。
总共 \(n^{cnt}\) 种赋值 \(x_i\) 的方式,而基环树不会再向外连边,自然也不会产生新的环。
至此基环树已经处理完了,只剩最后一种情况:树根向其他树连边。
由于树根连向树中的任意一个点本质相同,因此我们只需保留 \(sz\) 的信息(有 \(sz\) 种连边方式)。
假设有 \(m\) 棵树,第 \(i\) 棵树的大小为 \(a_i\) 。
考虑一个集合 \(S\) , \(|S|=k\) ,如果最终方案里要把 \(a_{s_1},a_{s_2},\cdots,a_{s_k}\) 串成一棵基环树,我们来统计一下方案数。
首先可以把这 \(k\) 棵树的串联顺序任意排列,由于是串成环所以方案数为 \((k-1)!\) 。
这一棵树的根节点可以接到下一棵树的任何一个点上,方案数为 \(a_{nxt_i}\) 。
而其他树的根节点的 \(x\) 可以随意赋值,所以还要乘上 \(n^{m-k}\) 。
因此总方案数为 \((k-1)!n^{m-k}\prod_{i=1}^ka_{s_i}\) 。
问题转化为计算:
动态规划, \(f_{i,j}\) 表示考虑前 \(i\) 个点,所有大小为 \(j\) 的集合 \(S\) 的 \(n^{i-j}\prod\limits_{k\in S}a_k\) 之和。
转移是容易的,时间复杂度 \(\mathcal O(n^2)\) 。
如果扩展到 \(n\le 10^5\) 怎么做?
用生成函数的视角看待这个问题,令 \(f(x)=\prod_{i=1}^m(n+a_ix)\) ,则 \(\sum_{i=0}^m(i-1)![x^i]f(x)\) 即为所求。
暴力求 \(f(x)\) 仍是 \(\mathcal O(n^2)\) ,但分治 \(\texttt{NTT}\) 可以做到 \(\mathcal O(n\log^2n)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2005,mod=998244353;
int m,n,cnt,res,tmp=1;
int f[maxn],x[maxn];
int a[maxn],sz[maxn],fac[maxn];
bool cir[maxn];
int dp[maxn][maxn];
int find(int x)
{
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
void merge(int u,int v)
{
u=find(u),v=find(v);
if(u==v) cir[u]=true;
else f[u]=v,sz[v]+=sz[u],cir[v]|=cir[u];
}
void add(int &x,long long y)
{
x=(x+y)%mod;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) f[i]=i,sz[i]=1;
for(int i=1;i<=n;i++)
{
scanf("%d",&x[i]);
if(x[i]!=-1) merge(i,x[i]);
else cnt++;
}
for(int i=1;i<=cnt;i++) tmp=1ll*n*tmp%mod;
for(int i=1;i<=n;i++)
{
if(find(i)!=i) continue;
if(cir[i]) add(res,tmp);
else a[++m]=sz[i];
}
fac[0]=dp[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<=i;j++)
{
dp[i][j]=1ll*n*dp[i-1][j]%mod;
if(j!=0) add(dp[i][j],1ll*dp[i-1][j-1]*a[i]);
}
for(int j=1;j<=m;j++) fac[j]=1ll*fac[j-1]*j%mod;
for(int j=1;j<=m;j++) add(res,1ll*fac[j-1]*dp[m][j]);
printf("%d",res);
return 0;
}
E. Not Equal Rectangle
题目描述
给定 \(n,m\) ,构造一个 \(n\times m\) 的矩阵 \(a\) ,满足:
- \(1\le a_{i,j}\le 25\) 。
- \(\forall 1\le x_1<x_2\le n,1\le y_1<y_2\le m\),满足 \(a_{x_1,y_1},a_{x_1,y_2},a_{x_2,y_1},a_{x_2,y_2}\) 不全相等。
数据范围
- \(2\le n,m\le 500\) 。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{1024MB}\) 。
分享
题面误导人系列,如果把 \(25\) 改成 \(23\) 会好做很多。
分块,将 \(p^2\times p^2\) 的平面划分成 \(p\times p\) 个块,每个块的大小为 \(p\times p\) 。
给第 \((i,j)\) 个块的第 \((k,l)\) 个位置赋值 \((i\cdot j+k+l)\bmod p+1\) 即可。
严谨证明咕了。
#include<bits/stdc++.h>
using namespace std;
const int v=23,maxn=530;
int m,n;
int a[maxn][maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<v;i++)
for(int j=0;j<v;j++)
for(int k=0;k<v;k++)
for(int l=0;l<v;l++)
a[i*v+k][j*v+l]=(i*j+k+l)%v;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
printf("%d%c",a[i][j]+1,j==m-1?'\n':' ');
return 0;
}
F - ABS Permutation (Count ver.)
题目描述
\(\forall k\in\{0,1\cdots,n-1\}\) ,求满足 \(\sum_{i=1}^{n-1}\big[|p_i-p_{i+1}|=m\big]=k\) 的排列 \(p\) 的个数。
数据范围
- \(2\le n\le 2.5\cdot 10^5\) 。
- \(1\le m\le n-1\) 。
时间限制 \(\texttt{8s}\) ,空间限制 \(\texttt{1024MB}\) 。
分析
恰好 \(=k\) 的方案数不好算,二项式反演转化为求钦定 \(k\) 个位置产生贡献的方案数 \(f_k\) 。
在值域上考虑这个问题,只有 \((i,i+m)\) 在原数组中相邻才会产生的贡献,因此我们可以先对 \(\bmod m\) 的每个剩余系分别算贡献,并且不同剩余系之间的贡献相互独立。
如果 \((i,i+m),(i+m,i+2m),\cdots\) 都能产生贡献,那就意味着 \(i,i+m,i+2m,\cdots\) 在数组中会排成一条链。贡献值为链长减一,并且如果链长 \(\ge 2\) ,这条链还可以翻转。
举个例子:假设 \(m=2\) ,则 \((1,3,5)\) 和 \((5,3,1)\) 都能产生贡献。
带着 \(m\) 讲不清楚,先考虑 \(m=1\) 的情况。
先考虑一个朴素的 \(\texttt{dp}\) 。
\(dp_{i,j,0/1}\) 表示考虑数值(不是数组下标) \(1\sim i\) ,当前贡献为 \(j\) , \((i-1,i)\) 是否产生贡献的方案数。
记录第三维状态是为了方便判断链长是否 \(\ge 2\) ,如果是则有两倍系数。
转移方程如下:
dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1]
dp[i][j][1]=2*dp[i-1][j-1][0]+dp[i-1][j-1][1]
上面的 \(\texttt{dp}\) 考虑了每条链的内容,但把这些链放到数组中还需要考虑这些链之间的顺序。
贡献为 \(j\) 意味着有 \(n-j\) 条链,这些链可以随便排,方案数 \((n-j)!\) 。
综上所述, \(f_j=(n-j)!(f_{n,j,0}+f_{n,j,1})\) 。
至此,我们可以在 \(\mathcal O(n^2)\) 的时间内解决 \(m=1\) 的问题。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2005,mod=998244353;
int m,n;
int fac[maxn],inv[maxn];
int f[maxn],g[maxn],dp[maxn][maxn][2];
int qpow(int a,int k)
{
int res=1;
for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
return res;
}
void init(int n)
{
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n;i>=1;i--) inv[i-1]=1ll*inv[i]*i%mod;
}
int c(int n,int m)
{
return m>=0&&m<=n?1ll*fac[n]*inv[m]%mod*inv[n-m]%mod:0;
}
int main()
{
scanf("%d%d",&n,&m),init(n);
dp[1][0][0]=1;
for(int i=2;i<=n;i++)
for(int j=0;j<=i;j++)
{
dp[i][j][0]=(dp[i-1][j][0]+dp[i-1][j][1])%mod;
if(j) dp[i][j][1]=(2ll*dp[i-1][j-1][0]+dp[i-1][j-1][1])%mod;
}
for(int j=0;j<n;j++) f[j]=1ll*fac[n-j]*(dp[n][j][0]+dp[n][j][1])%mod;
for(int j=0;j<n;j++)
{
int res=0;
for(int k=j;k<n;k++) res=(res+((k-j)&1?-1ll:1ll)*c(k,j)*f[k])%mod;
printf("%d%c",(res+mod)%mod," \n"[j==n-1]);
}
return 0;
}
暂时不管后面的二项式反演, \(\texttt{dp}\) 转移怎么优化?
第 \(i\) 层只用到第 \(i-1\) 层的信息,矩阵乘法?
\(2n\times 2n\) 的转移矩阵,开玩笑呢?
但是,如果把 \(j\) 压到生成函数里面,会产生意想不到的效果:
多项式矩阵快速幂,时间复杂度 \(T(n)=T(\frac n2)+\mathcal O(n\log n)\) ,解得 \(T(n)=\mathcal O(n\log n)\) ,只不过还有矩阵乘法的 \(8\) 倍常数。
优化二项式反演是个套路。
\(\texttt{NTT}\) 计算差卷积即可。
至此,我们可以在 \(\mathcal O(n\log n)\) 的时间内解决 \(m=1\) 的问题。
#include<bits/stdc++.h>
#define poly vector<int>
using namespace std;
const int maxn=1<<19,mod=998244353;
int m,n;
int fac[maxn],inv[maxn];
int qpow(int a,int k)
{
int res=1;
for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
return res;
}
void init(int n)
{
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n;i>=1;i--) inv[i-1]=1ll*inv[i]*i%mod;
}
int c(int n,int m)
{
return m>=0&&m<=n?1ll*fac[n]*inv[m]%mod*inv[n-m]%mod:0;
}
namespace Poly
{
int r[maxn],w[maxn];
int add(int x,int y)
{
if((x+=y)>=mod) x-=mod;
return x;
}
int dec(int x,int y)
{
if((x-=y)<0) x+=mod;
return x;
}
int extend(int n)
{
return 1<<(__lg(n-1)+1);
}
void get_r(int n)
{
for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|(i&1?n>>1:0);
}
void init(int n=maxn)
{
for(int k=2,m=1;k<=n;k<<=1,m<<=1)
{
w[m]=1;
for(int i=m+1,x=qpow(3,(mod-1)/k);i<k;i++) w[i]=1ll*w[i-1]*x%mod;
}
}
void print(poly a,int n)
{
a.resize(n);
for(int i=0;i<n;i++) printf("%d%c",a[i]," \n"[i==n-1]);
}
void ntt(poly &a,int n,int op)
{
for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int k=2,m=1;k<=n;k<<=1,m<<=1)
for(int i=0;i<n;i+=k)
for(int j=i,*x=w+m;j<i+m;j++,x++)
{
int v=1ll*a[j+m]**x%mod;
a[j+m]=dec(a[j],v),a[j]=add(a[j],v);
}
if(op==-1)
{
reverse(a.begin()+1,a.begin()+n);
int v=qpow(n,mod-2);
for(int i=0;i<n;i++) a[i]=1ll*a[i]*v%mod;
}
}
poly operator+(poly a,poly b)
{
int n=max(a.size(),b.size());a.resize(n),b.resize(n);
for(int i=0;i<n;i++) a[i]=add(a[i],b[i]);
return a;
}
poly operator*(poly a,poly b)
{
if(a.empty()||b.empty()) return {};
int n=a.size(),m=b.size(),len=extend(n+m-1);
a.resize(len),b.resize(len),get_r(len),ntt(a,len,1),ntt(b,len,1);
for(int i=0;i<len;i++) a[i]=1ll*a[i]*b[i]%mod;
return ntt(a,len,-1),a.resize(n+m-1),a;
}
}
using namespace Poly;
struct mat
{
poly a,b,c,d;
};
mat operator*(mat x,mat y)
{
return {x.a*y.a+x.b*y.c,x.a*y.b+x.b*y.d,x.c*y.a+x.d*y.c,x.c*y.b+x.d*y.d};
}
mat qpow(mat a,int k)
{
mat res={{1},{},{},{}};
for(;k;a=a*a,k>>=1) if(k&1) res=res*a;
return res;
}
int main()
{
scanf("%d%d",&n,&m),::init(n),Poly::init();
mat a={{1},{0,2},{1},{0,1}},tmp=qpow(a,n-1);
poly f=tmp.a+tmp.b,g(n+1);
f.resize(n);
for(int i=0;i<n;i++) f[i]=1ll*f[i]*fac[n-i]%mod*fac[i]%mod;
for(int i=0;i<n;i++) g[n-i]=(i&1?mod-1ll:1)*inv[i]%mod;
poly h=f*g;
for(int i=0;i<n;i++) printf("%lld%c",1ll*h[n+i]*inv[i]%mod," \n"[i==n-1]);
return 0;
}
问题并没有结束。如果 \(m\neq 1\) ,怎么办呢?
上面说过, \(\bmod m\) 的每个剩余系的贡献是独立的,又因为每个剩余系对应着一个多项式,所以直接把这 \(m\) 个多项式乘起来就行了。
有 n%m 个剩余系有 n/m+1 个元素, m-n%m 个剩余系有 n/m 个元素,多项式快速幂即可,时间复杂度 \(T(n)=T(\frac n2)+\mathcal O(n\log n)\) ,解得 \(T(n)=\mathcal O(n\log n)\) 。
完整时间复杂度 \(\mathcal O(n\log n)\) 。
#include<bits/stdc++.h>
#define poly vector<int>
using namespace std;
const int maxn=1<<19,mod=998244353;
int m,n;
int fac[maxn],inv[maxn];
int qpow(int a,int k)
{
int res=1;
for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
return res;
}
void init(int n)
{
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n;i>=1;i--) inv[i-1]=1ll*inv[i]*i%mod;
}
int c(int n,int m)
{
return m>=0&&m<=n?1ll*fac[n]*inv[m]%mod*inv[n-m]%mod:0;
}
namespace Poly
{
int r[maxn],w[maxn];
int add(int x,int y)
{
if((x+=y)>=mod) x-=mod;
return x;
}
int dec(int x,int y)
{
if((x-=y)<0) x+=mod;
return x;
}
int extend(int n)
{
return n!=1?1<<(__lg(n-1)+1):1;
}
void get_r(int n)
{
for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|(i&1?n>>1:0);
}
void init(int n=maxn)
{
for(int k=2,m=1;k<=n;k<<=1,m<<=1)
{
w[m]=1;
for(int i=m+1,x=qpow(3,(mod-1)/k);i<k;i++) w[i]=1ll*w[i-1]*x%mod;
}
}
void print(poly a,int n)
{
a.resize(n);
for(int i=0;i<n;i++) printf("%d%c",a[i]," \n"[i==n-1]);
}
void ntt(poly &a,int n,int op)
{
for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int k=2,m=1;k<=n;k<<=1,m<<=1)
for(int i=0;i<n;i+=k)
for(int j=i,*x=w+m;j<i+m;j++,x++)
{
int v=1ll*a[j+m]**x%mod;
a[j+m]=dec(a[j],v),a[j]=add(a[j],v);
}
if(op==-1)
{
reverse(a.begin()+1,a.begin()+n);
int v=qpow(n,mod-2);
for(int i=0;i<n;i++) a[i]=1ll*a[i]*v%mod;
}
}
poly operator+(poly a,poly b)
{
int n=max(a.size(),b.size());a.resize(n),b.resize(n);
for(int i=0;i<n;i++) a[i]=add(a[i],b[i]);
return a;
}
poly operator*(poly a,poly b)
{
if(a.empty()||b.empty()) return {};
int n=a.size(),m=b.size(),len=extend(n+m-1);
a.resize(len),b.resize(len),get_r(len),ntt(a,len,1),ntt(b,len,1);
for(int i=0;i<len;i++) a[i]=1ll*a[i]*b[i]%mod;
return ntt(a,len,-1),a.resize(n+m-1),a;
}
}
using namespace Poly;
struct mat
{
poly a,b,c,d;
};
mat operator*(mat x,mat y)
{
return {x.a*y.a+x.b*y.c,x.a*y.b+x.b*y.d,x.c*y.a+x.d*y.c,x.c*y.b+x.d*y.d};
}
poly calc(int n)
{
mat a={{1},{0,2},{1},{0,1}},res={{1},{},{},{}};
for(int k=n-1;k;a=a*a,k>>=1) if(k&1) res=res*a;
return res.a+res.b;
}
poly qpow(poly a,int k)
{
poly res={1};
for(;k;a=a*a,k>>=1) if(k&1) res=res*a;
return res;
}
int main()
{
scanf("%d%d",&n,&m),::init(n),Poly::init();
poly f=qpow(calc(n/m+1),n%m)*qpow(calc(n/m),m-n%m),g(n+1);
f.resize(n);
for(int i=0;i<n;i++) f[i]=1ll*f[i]*fac[n-i]%mod*fac[i]%mod;
for(int i=0;i<n;i++) g[n-i]=(i&1?mod-1ll:1)*inv[i]%mod;
poly h=f*g;
for(int i=0;i<n;i++) printf("%lld%c",1ll*h[n+i]*inv[i]%mod," \n"[i==n-1]);
return 0;
}
总结
- 场上 \(41\min\) 切了前\(3\)题,然后对着 \(\texttt D\) 和 \(\texttt E\) 罚坐到比赛结束,感觉还是思维能力不足吧。
- 这场的 \(\texttt F\) 题是真的毒瘤,动态规划 + 多项式矩阵快速幂 + 多项式快速幂 + 二项式反演差卷积,博主刚了一天才完全弄懂,另外 \(\texttt F\) 题题解长度大约占了本文一半。
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16279226.html
浙公网安备 33010602011771号