CF1808E Minibuses on Venus 智商毁灭记

都要考省选了大脑还在这里下线

场上看到这道题很快推出了 k 为奇数的搞法,发现可以直接做到 O(klogn),一阵狂喜然后肝起了 E3,结果 E1 都没过。

事实上这道题可以直接做到 O(logn),不过需要细致的观察自己场上推的式子。

题意:

对长度为 n,值域为 [0,k) 的整数数组 a 计数,要求 a 满足 i[1,n],jiajai(modk)

思路:

这道题的分析频繁用到了一次同余方程式的有解性分析,也就是说对于 ab(modk),当 gcd(a,k)|b 时有 gcd(a,k) 个解,否则无解。

显然可以先枚举一波 sum=(i=1nai)modk,那么题目的条件相当于 i[1,n],2aisum(modk)

k 是奇数时上述方程始终有唯一解,题目的限制相当于强制 sum2 必须出现。

考虑计数强制 x=sum2 必须出现,然后对着这个限制上容斥,钦定 x 至少出现了 i 次。

剩下的部分如果 ni>0,那么通过调整最后一个数的取值总可以满足 i=1naisum(modk)。如果 i=n 那么只需要判断 nx 是否同余于 sum

方案数就是:

kn1((1)n[nxsum(modk)]+i=0n1(ni)(1)ikni1)

对于所有 sum=2x,考虑 [nx2x(modk)] 的有解性,总方案数就是:

kn(k1)n+(1)n(1)ngcd(n2,k)

k 是偶数时更复杂一点。这时 2aisum(modk)sum 是偶数时有两个解,记为 xx+k2

现在要求序列中这两个解必须至少出现一个,那么考虑计数两个解都没有出现的方案数。

依然考虑对于“没有出现”这一限制施加容斥,枚举 xx+k2 的出现次数。

i=0nj=0n[i+j=n](1)i(1)j[ix+j(x+k2)sum(modk)]+i=0nj=0n(ni,j,nij)(1)i(1)jknij1[i+j<n]

用三项式定理做一下右边的部分,就是:

(k2)n(2)nk

左边的线性同余方程的解是一个等差数列,相当于是要组合数等差数列位置的和。这里场后被 zhy 教育了,可以直接循环卷积快速幂 O(k2logn) 做。

然而这个同余式太有性质了!整理一下:

[ix+(ni)(x+k2)2x(modk)]

也就是:

[(2n)x(ni)k2(modk)]

右边只与 i 奇偶性有关!左边只与 n,x 有关!而众所周知组合数奇数偶数位置求和都是 2n1

我们现在只需要求出满足 [(2n)x0(modk)][(2n)xk2(modk)]x 的个数。

注意 x 本来就满足 x<k2可以统一写成 [(2n)x0(modk2)]x 的个数就是 gcd(k2,n2)

总的式子就是:

kn2(k2)n(2)n2(1)ngcd(n2,k2)2n1

这道题就做完了。

#include <cstdio>
using namespace std;
template<typename T>
T read(){
char c=getchar();T x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
typedef long long ll;
ll n;int k,p;
int qp(int a,ll b){
int res=1;
b%=(p-1);
while(b){
if(b&1) res=(ll)res*a%p;
a=(ll)a*a%p;b>>=1;
}
return res;
}
ll gcd(ll a,ll b){
if(!b) return a;
return gcd(b,a%b);
}
void inc(int &x,int v){if((x+=v)>=p) x-=p;}
void dec(int &x,int v){if((x-=v)<0) x+=p;}
int main(){
n=read<ll>();k=read<int>();p=read<int>();
if(n==1){puts("1");return 0;}
if(k&1){
int g=gcd(n-2,k);
int res=qp(k,n);
dec(res,qp(k-1,n));
if(n&1) inc(res,g-1);
else dec(res,g-1);
printf("%d\n",res);
}
else{
int g=gcd(n-2,k>>1);
int res=qp(k,n);
dec(res,qp(k-2,n));
inc(res,qp(p-2,n));
if(res&1) res=(res+p)>>1;
else res>>=1;
if(n&1) inc(res,(ll)qp(2,n-1)*g%p);
else dec(res,(ll)qp(2,n-1)*g%p);
printf("%d\n",res);
}
return 0;
}
posted @   yyyyxh  阅读(88)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示