蜜月日记 Part11
littlez_meow · · 休闲·娱乐
前言
暂且跳过 Day 100,先往后写吧。
Day 101
复健博弈论。
2025/04/14 [SNOI2020] 取石子
题意
有
思路
比较板的一道博弈论。
首先
我们就有如下定理:
【齐肯多夫定理】 任意正整数可以表示为若干不相邻斐波那契数之和。这种表示方法称为齐肯多夫表示法。
然后在没有
【Fibonacci Nim】 有
加上
那么问题变成求
时间复杂度
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
using namespace std;
const int MAXN=90;
ll fib[MAXN],n,k,dp[MAXN][2][2];
int lim,T;
bool nn[MAXN];
ll dfs(int now,bool eq,bool lst){
if(now<=lim) return 1;
if(dp[now][eq][lst]!=-1) return dp[now][eq][lst];
ll ans=0;
if(nn[now]){
if(!lst) ans+=dfs(now-1,eq,1);
ans+=dfs(now-1,0,0);
}else{
if(!eq&&!lst) ans+=dfs(now-1,0,1);
ans+=dfs(now-1,eq,0);
}
return dp[now][eq][lst]=ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
fib[1]=1;
F(i,2,89) fib[i]=fib[i-1]+fib[i-2];
for(cin>>T;T;--T){
cin>>k>>n;
--n;
memset(nn,0,sizeof nn),memset(dp,-1,sizeof dp);
ll qwq=n;
R(i,89,1) if(qwq>=fib[i]) nn[i]=1,qwq-=fib[i];
R(i,89,0) if(fib[i]<=k){lim=i;break;}
cout<<n-dfs(89,1,0)+1<<"\n";
}
return 0;
}
Day 102
复健生成函数。
2025/04/17 数树(2021 CoE-II E)
题意
定义一棵无标号有根树是
思路
就喜欢这种儿子之间有顺序的树,可以直接上生成函数。
由于我们还要知道
直接就有
移项变成
上拉反:
即
我们需要对于所有
枚举
预处理阶乘和阶乘逆元,计算时间复杂度
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
using namespace std;
const int MAXN=1e7+1,MOD=998244353;
int n,a,b,k1,k2,fact[MAXN],inv[MAXN];
inline ll qpow(ll b,int e){ll res=1;while(e)(e&1)&&(res=res*b%MOD),b=b*b%MOD,e>>=1;return res;}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>k1>>k2>>n>>a>>b;
ll invn=qpow(n,MOD-2);
fact[0]=1;
F(i,1,n) fact[i]=fact[i-1]*1ll*i%MOD;
inv[n]=qpow(fact[n],MOD-2);
R(i,n,1) inv[i-1]=inv[i]*1ll*i%MOD;
int ch=0,mo=0;
F(k,0,n){
ll r,s=k,t=n-1-s*k1;
if(t%k2) continue;
t=t/k2,r=n-s-t;
if(r<0||s<0||t<0) continue;
ll qwq=invn*fact[n]%MOD*inv[r]%MOD*inv[s]%MOD*inv[t]%MOD;
ch=(ch+(s*a+t*b)%MOD*qwq)%MOD;
(mo+=qwq)>=MOD&&(mo-=MOD);
}
cout<<ch*qpow(mo,MOD-2)%MOD;
return 0;
}
Day 103
概率不取模,肯定有问题。
2025/04/18 [PA 2025] 考试 / Egzamin
题意
有
思路
首先
于是设
转移显然为
发现有很多
为什么这样做时间复杂度是对的?
我们需要如下定理:
【中心极限定理】 对于足够多独立同分布的随机变量,若它们的均值为
本题中,每道题的得分服从两点分布,方差可以视为常数。根据中心极限定理,总得分应服从正态分布。
如果你不会很多概率论知识,我们可以用人教版数学选择性必修三的内容解释。数学书上提到了
如果你学过概率论,你应该听说过切尔诺夫引理。具体内容比较复杂就不展开了,这个引理表明
代码
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define fi first
#define se second
using namespace std;
const int MAXN=5e4+1;
const double EPS=1e-12;
int n,t;
double p[MAXN],ans[MAXN];
__gnu_pbds::cc_hash_table<int,double>dp[2];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>t;
F(i,1,n) cin>>p[i];
sort(p+1,p+n+1,greater<double>());
dp[0][n]=1;
F(i,0,n-1){
int now=i&1,nxt=now^1;
dp[nxt].clear();
for(auto qwq:dp[now]) if(qwq.se>EPS){
if(qwq.fi>=n+t) ans[i]+=qwq.se;
dp[nxt][qwq.fi+1]+=qwq.se*p[i+1];
dp[nxt][qwq.fi-1]+=qwq.se*(1-p[i+1]);
}
}
for(auto qwq:dp[n&1]) if(qwq.se>EPS) if(qwq.fi>=n+t) ans[n]+=qwq.se;
double res=0;
F(i,1,n) res=max(res,ans[i]);
cout<<fixed<<setprecision(10)<<res;
return 0;
}
Day 104
有关这个 3B1B 视频的题目数量还在提升。
2025/04/20 二平方和定理
题意
求
思路
我们把问题丢到复平面上,就是求
考虑在高斯整环
介绍如下定理:
【费马平方和定理】 奇质数
根据费马平方和定理,对于一个奇质数
也就是说,我们只需要把
设奇质数
根据二次剩余相关知识,
由于高斯整环是辗转相除环,这里的
那么我们已经可以把
先处理模
然后是模
最后是
时间复杂度为
代码
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define int ll
#define fi first
#define se second
using namespace std;
__gnu_pbds::gp_hash_table<ll,int>e;
inline ll qpow(__int128 base,ll expo,const ll MOD){
ll res(1);
while(expo){
(expo&1)&&(res=res*base%MOD);
base=base*base%MOD,expo>>=1;
}
return res;
}
namespace Factorize{
const int MR_Prime[]={2,325,9375,28178,450775,9780504,1795265022};
inline bool MR(ll n){
if(n<=2) return n==2;
if(!(n&1)) return 0;
ll expo=n-1;
int r=__builtin_ctzll(expo);
expo>>=r;
for(int i:MR_Prime){
ll v=qpow(i,expo,n);
if(v<=1||v==n-1) continue;
F(j,1,r){
v=__int128(v)*v%n;
if(v==n-1&&j!=r){
v=1;
break;
}
if(v==1) return 0;
}
if(v!=1) return 0;
}
return 1;
}
__int128 gcd(__int128 x,__int128 y){return y==0?x:gcd(y,x%y);}
__uint128_t SQUFOF(__uint128_t value) {
__uint128_t s = sqrtl(1.0l * value) + 1e-6;
while (s * s > value) s--;
if (s * s == value) return s;
__int128 d, po, p, p_prev, q, q_prev, q_, b, r;
long long l, b_, i;
int k = 0;
l = 2 * sqrtl(2.0l * s);
b_ = 3 * l;
while (++k) {
d = k * value;
po = p_prev = p = sqrtl(1.0l * d);
q_prev = 1;
q = d - po * po;
while (q < 0) {
po--;
p_prev--;
p--;
q = d - po * po;
}
if (!q) return gcd(value, k);
for (i = 2; i < b_; i++) {
b = (po + p) / q;
p = b * q - p;
q_ = q;
q = q_prev + b * (p_prev - p);
r = sqrtl(1.0 * q) + 1e-6;
if (!(i & 1) && r * r == q) break;
q_prev = q_;
p_prev = p;
}
if (i >= b_) continue;
b = (po - p) / r;
p_prev = p = b * r + p;
q_prev = r;
q = (d - p_prev * p_prev) / q_prev;
i = 0;
do {
b = (po + p) / q;
p_prev = p;
p = b * q - p;
q_ = q;
q = q_prev + b * (p_prev - p);
q_prev = q_;
i++;
} while (p != p_prev && i < b_);
if (i >= b_) continue;
r = gcd(value, q_prev);
if (r != 1) return r;
}
return 0;
}
inline void factorize(ll n,ll cnt=1){
if(n==1) return;
if(MR(n)) return e[n]+=cnt,void();
ll qaq=SQUFOF(n);
while(qaq==1||qaq==n) qaq=SQUFOF(n);
int nc=0;
while(!(n%qaq)) n/=qaq,++nc;
factorize(n,cnt),factorize(qaq,nc*cnt);
return;
}
}
struct Cpx{
__int128 a,b;//a+bi;
Cpx(const ll&x=0,const ll&y=0):a(x),b(y){}
Cpx operator+(const Cpx&x)const{return Cpx(a+x.a,b+x.b);}
Cpx operator-(const Cpx&x)const{return Cpx(a-x.a,b-x.b);}
Cpx operator*(const Cpx&x)const{return Cpx(a*x.a-b*x.b,a*x.b+b*x.a);}
Cpx operator/(const Cpx&x)const{
double qwq=x.a*x.a+x.b*x.b;
return Cpx(round((a*x.a+b*x.b)/qwq),round((b*x.a-a*x.b)/qwq));
}
Cpx operator%(const Cpx&x)const{
return *this-x*(*this/x);
}
Cpx operator^(ll ex)const{
Cpx res(1),b=*this;
while(ex) (ex&1)&&(res=res*b,1),b=b*b,ex>>=1;
return res;
}
bool operator==(const Cpx&x)const{return a==x.a&&b==x.b;}
__int128 abs()const{return a*a+b*b;}
};
Cpx gcd(const Cpx&x,const Cpx&y){
if(x.abs()<y.abs()) return gcd(y,x);
return y==Cpx()?x:gcd(y,x%y);
}
int cnt,ex[100];
ll pr[100];
Cpx coef[100];
mt19937 gen(114514);
vector<pair<ll,ll> >ans;
void dfs(int step,const Cpx&now){
if(step==cnt+1){
ll x=now.a,y=now.b;
if(x>=0&&y>=0) ans.emplace_back(x,y);
if(x<=0&&y<=0) ans.emplace_back(-x,-y);
if(y<=0&&x>=0) ans.emplace_back(-y,x);
if(y>=0&&x<=0) ans.emplace_back(y,-x);
return;
}
if((pr[step]&3)!=1) dfs(step+1,now*coef[step]);
else{
Cpx bar(coef[step].a,-coef[step].b),qwq=bar^ex[step];
F(i,0,ex[step]) dfs(step+1,now*qwq),qwq=qwq/bar*coef[step];
}
return;
}
inline void solve(){
cnt=0;
for(auto i:e){
pr[++cnt]=i.fi,ex[cnt]=i.se;
if(i.fi==2) coef[cnt]=Cpx(1,1)^i.se;
else if((i.fi&3)==1){
ll qwq;
while(1){
qwq=gen()%(i.fi-1)+1;
if(qpow(qwq,i.fi>>1,i.fi)==i.fi-1)break;
}
qwq=qpow(qwq,i.fi>>2,i.fi);
coef[cnt]=gcd(Cpx(i.fi),Cpx(qwq,1));
}else if(i.se&1) return cout<<"0\n",void();
else coef[cnt]=Cpx(qpow(i.fi,i.se>>1,1e18));
}
vector<pair<ll,ll> >().swap(ans);
dfs(1,Cpx(1,0));
sort(ans.begin(),ans.end());
cout<<ans.size()<<"\n";
for(auto i:ans) cout<<i.fi<<" "<<i.se<<"\n";
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll T,n;
for(cin>>T;T;--T){
e.clear();
cin>>n;
if(n<=1e10){
for(int i=2;i*i<=n;++i) if(n%i==0) while(n%i==0) n/=i,++e[i];
if(n!=1) ++e[n];
}else Factorize::factorize(n);
solve();
}
return 0;
}
Day 105
感觉自己组合数学有很多知道但是不会推的结论。
2025/04/22 「EZEC-5」「KrOI2021」Chasse Neige 及其加强版
题意
求长为
思路
首先非常显然是排列插入 dp,设
你发现
所以设
然后发现数据范围里
而这是经典的 zig-zag 排列计数,我们有
使用多项式求逆,预处理所有答案,时间复杂度
可能需要 DIF-DIT 写法的 NTT 来卡常。
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
using namespace std;
const int MAXN=(1<<23)+5,MOD=998244353,G=3;
inline ll qpow(ll base,ll expo){
ll res=1;
while(expo){
(expo&1)&&(res=res*base%MOD);
base=base*base%MOD,expo>>=1;
}
return res;
}
const int INVG=qpow(G,MOD-2);
ll powg[MAXN];
inline void init(){
powg[0]=1;
ll qwq=qpow(G,(MOD-1)>>23);
F(i,0,21){
ll qaq=qpow(qwq,1<<(21-i));
F(j,1<<i,(1<<(i+1))-1) powg[j]=qaq*powg[j-(1<<i)]%MOD;
}
return;
}
inline void DIF(int*poly,int len){
for(int i=len>>1;i>=1;i>>=1) for(int j=0,t=0;j<len;j+=(i<<1),++t) F(k,0,i-1){
ll x=poly[j|k],y=poly[i|j|k]*1ll*powg[t]%MOD;
(poly[j|k]=x+y)>=MOD&&(poly[j|k]-=MOD);
(poly[i|j|k]=x-y)<0&&(poly[i|j|k]+=MOD);
}
return;
}
inline void DIT(int*poly,int len){
for(int i=1;i<len;i<<=1) for(int j=0,t=0;j<len;j+=(i<<1),++t) F(k,0,i-1){
ll x=poly[j|k],y=poly[i|j|k];
(poly[j|k]=x+y)>=MOD&&(poly[j|k]-=MOD);
poly[i|j|k]=(MOD+x-y)*powg[t]%MOD;
}
ll invl=qpow(len,MOD-2);
F(i,0,len-1) poly[i]=poly[i]*invl%MOD;
reverse(poly+1,poly+len);
return;
}
inline void inv(int*coef,int*res,int len){
res[0]=qpow(coef[0],MOD-2);
for(int l=2;l<=len<<1;l<<=1){
static int tmp[MAXN];
int qwq=l<<1;
memcpy(tmp,coef,sizeof(int)*l);
memset(tmp+l,0,sizeof(int)*(qwq-l));
DIF(tmp,qwq),DIF(res,qwq);
F(i,0,qwq-1) (res[i]=(2-res[i]*1ll*tmp[i])%MOD*res[i]%MOD)<0&&(res[i]+=MOD);
DIT(res,qwq);
memset(res+l,0,sizeof(int)*(qwq-l));
}
F(i,len+1,len<<1) res[i]=0;
return;
}
int T,n,k,fact[MAXN],finv[MAXN],mo[MAXN],im[MAXN],ch[MAXN],f[21][MAXN];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
init();
cin>>T>>n;
n+=20;
fact[0]=1;
F(i,1,n+1) fact[i]=fact[i-1]*1ll*i%MOD;
finv[n+1]=qpow(fact[n+1],MOD-2);
R(i,n+1,1) finv[i-1]=finv[i]*1ll*i%MOD;
for(int i=0,v=0;i<=n+1;i+=2,v^=1){
mo[i]=(v?MOD-finv[i]:finv[i]);
i<=n&&(ch[i+1]=(v?MOD-finv[i+1]:finv[i+1]));
}
ch[0]=1;
inv(mo,im,n+1);
int len=1<<(__lg(2*n+1)+1);
DIF(im,len),DIF(ch,len);
F(i,0,len-1) ch[i]=ch[i]*1ll*im[i]%MOD;
DIT(ch,len);
F(i,0,n) f[1][i]=fact[i]*1ll*ch[i]%MOD;
static ll iv[21]={0,1};
F(j,2,20) iv[j]=MOD-MOD/j*iv[MOD%j]%MOD;
F(j,1,19) F(i,1,n) (f[j+1][i-1]=(f[j][i]-(i-j+0ll)*f[j-1][i-1]-2ll*f[j][i-1])%MOD*iv[j]%MOD)<0&&(f[j+1][i-1]+=MOD);
for(;T;--T){
cin>>n>>k;
cout<<f[n-2*k][n]<<"\n";
}
return 0;
}
Day 106
是不是好久没放交互了。
2025/04/26 [RMI 2020] 寻线 / Nice Lines
题意
有
思路
计算几何值域却开这么小,肯定有说法。下面记
如果我们能找到一个
那么我们就只询问直线
考虑分治。设当前分治到值域区间
一个非常自然的想法是把分治从值域上搬到拐点序列上。我们希望如果只有区间内只有一个拐点就找到它并且不再递归。首先,我们在足够大和足够小的坐标上找到区间内最靠左和最靠右的组成凸壳的直线,找到它们的交点
这里说的在某处找到组成凸壳的直线可以使用两次询问直接求斜率。当
分析这样的询问次数,我们初始要
代码
#include<bits/stdc++.h>
using namespace std;
long double query(long double x, long double y);
void the_lines_are(std::vector<int> a, std::vector<int> b);
namespace MySol{
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define ll long long
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define double long double
#define Line pair<double,double>
#define k first
#define b second
const int x0=1e5,V=2e4;
const double EPS=1e-5;
int n;
vector<int>ansa,ansb;
inline Line find(double t){
ll qwq=floor(t/x0);
double x=qwq*x0+V,y=query(x0,x),k=(y-query(x0,(qwq+1)*x0-V))/(2*V-x0);
return {k,y-k*x};
}
void solve(Line l,Line r){
double mid=(r.b-l.b)/(l.k-r.k),fmid=query(x0,mid);
if(fabsl(l.k*mid+l.b-fmid)<EPS&&fabsl(r.k*mid+r.b-fmid)<EPS){
ll k=round(mid/x0);
ansa.push_back(k),ansb.push_back(round(mid-x0*k));
return;
}
Line m=find(mid);
solve(l,m),solve(m,r);
return;
}
void main(){
solve(find(-2e9),find(2e9));
the_lines_are(ansa,ansb);
return;
}
#undef double
}
void solve(int subtask_id, int N){MySol::n=N;MySol::main();}
Day 107
之前被卡常了,拿新多项式板子写写。
2025/05/05 [集训队互测 2012] calc 及其加强版
题意
给定
思路
求出无序的答案再乘
显然可以写出答案的生成函数为:
使用指数对数技巧化简为:
接下来处理
使用处理对数的经典技巧,先求导再积分,我们有
对积分里面的做幂级数展开,交换积分与求和号:
把积分积出来:
补上一个
交换求和号:
只要能快速求一列自然数幂和就能求出这个多项式。考虑到伯努利数的形式,我们分析一列自然数幂和的 EGF:
交换求和号得到
发现求和号后是
上多项式全家桶即可
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
using namespace std;
const int MAXN=(1<<22)+5,MOD=998244353,G=3;
inline ll qpow(ll base,ll expo){
ll res=1;
while(expo){
(expo&1)&&(res=res*base%MOD);
base=base*base%MOD,expo>>=1;
}
return res;
}
const int INVG=qpow(G,MOD-2);
ll powg[MAXN];
inline void init(){
powg[0]=1;
ll qwq=qpow(G,(MOD-1)>>23);
F(i,0,21){
ll qaq=qpow(qwq,1<<(21-i));
F(j,1<<i,(1<<(i+1))-1) powg[j]=qaq*powg[j-(1<<i)]%MOD;
}
return;
}
inline void DIF(int*poly,int len){
for(int i=len>>1;i>=1;i>>=1) for(int j=0,t=0;j<len;j+=(i<<1),++t) F(k,0,i-1){
ll x=poly[j|k],y=poly[i|j|k]*1ll*powg[t]%MOD;
(poly[j|k]=x+y)>=MOD&&(poly[j|k]-=MOD);
(poly[i|j|k]=x-y)<0&&(poly[i|j|k]+=MOD);
}
return;
}
inline void DIT(int*poly,int len){
for(int i=1;i<len;i<<=1) for(int j=0,t=0;j<len;j+=(i<<1),++t) F(k,0,i-1){
ll x=poly[j|k],y=poly[i|j|k];
(poly[j|k]=x+y)>=MOD&&(poly[j|k]-=MOD);
poly[i|j|k]=(MOD+x-y)*powg[t]%MOD;
}
ll invl=qpow(len,MOD-2);
F(i,0,len-1) poly[i]=poly[i]*invl%MOD;
reverse(poly+1,poly+len);
return;
}
inline void Inv(int*coef,int*res,int len){
res[0]=qpow(coef[0],MOD-2);
for(int l=2;l<=len<<1;l<<=1){
static int tmp[MAXN];
int qwq=l<<1;
memcpy(tmp,coef,sizeof(int)*l);
memset(tmp+l,0,sizeof(int)*(qwq-l));
DIF(tmp,qwq),DIF(res,qwq);
F(i,0,qwq-1) (res[i]=(2-res[i]*1ll*tmp[i])%MOD*res[i]%MOD)<0&&(res[i]+=MOD);
DIT(res,qwq);
memset(res+l,0,sizeof(int)*(qwq-l));
}
F(i,len+1,len<<1) res[i]=0;
return;
}
int fact[MAXN],finv[MAXN],inv[MAXN],ans[MAXN],res[MAXN];
inline void Exp(int l,int r){
if(l+1==r){
if(l) ans[l]=ans[l]*1ll*inv[l]%MOD;
else ans[l]=1;
return;
}
int mid=(l+r)>>1;
Exp(l,mid);
static int f[MAXN],g[MAXN],len;
len=1<<(__lg(r-l)+1);
F(i,0,mid-l-1) f[i]=ans[i+l];
F(i,0,r-l-2) g[i]=res[i];
F(i,mid-l,len-1) f[i]=0;
F(i,r-l-1,len-1) g[i]=0;
DIF(f,len),DIF(g,len);
F(i,0,len-1) f[i]=f[i]*1ll*g[i]%MOD;
DIT(f,len);
F(i,mid,r-1) (ans[i]+=f[i-l-1])>=MOD&&(ans[i]-=MOD);
Exp(mid,r);
}
int k,m,mo[MAXN],ch[MAXN];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
init();
cin>>k>>m;
m+=2;
fact[0]=fact[1]=finv[0]=inv[1]=1;
F(i,2,m) fact[i]=fact[i-1]*1ll*i%MOD,inv[i]=MOD-MOD/i*1ll*inv[MOD%i]%MOD;
F(i,1,m) finv[i]=finv[i-1]*1ll*inv[i]%MOD;
int l=1<<(__lg(2*m)+1);
F(i,1,m) ch[i-1]=finv[i];
Inv(ch,mo,m);
ll pw=k+1;
F(i,1,m) ch[i-1]=finv[i]*(pw-1)%MOD,pw=pw*(k+1)%MOD;
DIF(ch,l),DIF(mo,l);
F(i,0,l-1) ch[i]=ch[i]*1ll*mo[i]%MOD;
DIT(ch,l);
F(j,1,m) res[j]=((j&1)?1ll:MOD-1ll)*inv[j]%MOD*ch[j]%MOD*fact[j]%MOD;
F(j,1,m) res[j-1]=res[j]*1ll*j%MOD;
res[m]=0;
Exp(0,m);
F(i,1,m-2) cout<<ans[i]*1ll*fact[i]%MOD<<"\n";
return 0;
}
Day 108
感觉很典的逻辑题。
2025/05/18 [PA 2022] Mędrcy
题意
有
思路
既然是
一个人如果什么都不知道,他第
如果
类似的,如果删掉
由于是求最早走的,相当于是求这个图的所有最小点覆盖。如果最小点覆盖
这显然不存在多项式做法。由于
设现在最小点覆盖还有
枚举度数最大的点在或不在最小点覆盖中,设其度数为
总复杂度
代码
#include<bits/stdc++.h>
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
using namespace std;
const int MAXN=1005;
int n,m,k,deg[MAXN];
vector<int>g[MAXN];
bitset<MAXN>ans,isin;
inline void mdf(int now,int v){for(int i:g[now]) deg[i]+=v;}
bool reach[MAXN],del[MAXN];
vector<int>p[MAXN];
void find(int now,int bg){
reach[now]=1;p[bg].push_back(now);
for(int i:g[now]) if(!reach[i]&&!del[i]) find(i,bg);
}
int mn,now;
void dfs(){
if(now>mn) return;
int qwq=0;
F(i,1,n) if(!del[i]&°[i]>deg[qwq]) qwq=i;
if(!qwq){
if(now<mn) ans.reset(),mn=now;
if(now==mn) ans|=isin;
return;
}else if(deg[qwq]>2){
++now,isin[qwq]=del[qwq]=1,mdf(qwq,-1);
dfs();
isin[qwq]=0,--now;
vector<int>ne;
for(int i:g[qwq]) if(!del[i]) ne.push_back(i),isin[i]=del[i]=1,mdf(i,-1),++now;
dfs();
for(int i:ne) isin[i]=del[i]=0,mdf(i,1),--now;
del[qwq]=0,mdf(qwq,1);
}else{
memset(reach,0,n+1);
vector<int>chain,cycle;
int delta=0;
F(i,1,n) if(!reach[i]&&!del[i]&°[i]<=1){
vector<int>().swap(p[i]);
find(i,i);
chain.push_back(i);
delta+=(p[i].size()>>1);
}
F(i,1,n) if(!reach[i]&&!del[i]){
vector<int>().swap(p[i]);
find(i,i);
cycle.push_back(i);
delta+=(p[i].size()-1)/2+1;
}
now+=delta;
if(now<mn) ans.reset(),mn=now;
if(now==mn){
ans|=isin;
for(int i:chain){
if(p[i].size()&1){
bool flag=0;
for(int j:p[i]){
if(flag) ans[j]=1;
flag^=1;
}
}else for(int j:p[i]) ans[j]=1;
}
for(int i:cycle) for(int j:p[i]) ans[j]=1;
}
now-=delta;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;
for(cin>>T;T;--T){
F(i,1,n) vector<int>().swap(g[i]);
cin>>n>>m>>k,mn=k+1;
unordered_set<int>vis;
F(i,1,m){
int u,v;
cin>>u>>v;
if(u>v) swap(u,v);
if(vis.find(u*(n+1)+v)!=vis.end()) continue;
else vis.insert(u*(n+1)+v);
g[u].push_back(v),g[v].push_back(u);
}
F(i,1,n) deg[i]=g[i].size();
ans.reset();
dfs();
if(mn==k+1) cout<<"-1\n";
else{
cout<<mn<<" "<<ans.count()<<"\n";
F(i,1,n) if(ans[i]) cout<<i<<" ";
cout<<"\n";
}
}
return 0;
}
Day 109
每次看到新的模拟费用流题,都会觉得之前蜜月日记里总结的模拟费用流非常不典型。感觉之前蜜月日记里的全都只能是反悔贪心,没有用到费用流建模出的图,最多只用到了流量。所以放一道从建模的图来分析的模拟费用流。
2025/05/21 [Ynoi Easy Round 2018] 星野瑠美衣
题意
你有一个
思路
下面认为
先来研究
数轴上我们把权值为
-
当
x<\min\{x_1,x_2\} ,变化量为-|x_1-x_2|+x_1+x_2-2x+v ; -
当
\min\{x_1,x_2\}\le x\le \max\{x_1,x_2\} ,变化量为v ; -
当
x>\max\{x_1,x_2\} ,变化量为-|x_1-x_2|-x_1-x_2+2x+v 。
画图发现,我们可以不管前提条件,直接对这三个式子取
再加上
那么就可以进行费用流建模了。
我们一共有五类点:源点
有如下四类容量均为
点数边数都是
直接费用流跑不过去,考虑模拟费用流,我们每次找一条增广路。
这张图是二分图,左部点有
那么直接维护任意两个左部点之间经过一个右部点的最长路,以这个为边权在左部点内部跑 SPFA,找出来增广路后暴力修改影响到的路径。我们维护任意两个左部点之间经过一个右部点的所有路径,每次增广是把
时间复杂度
代码
#include<bits/stdc++.h>
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define fi first
#define se second
using namespace std;
const int MAXN=2e5+1;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n,m,px[MAXN],py[MAXN],x[MAXN],y[MAXN],v[MAXN];
//S->0,T->10
priority_queue<pair<ll,int> >q[11][11],del[11][11];
ll ans,val[11][MAXN],dis[11][11],dep[11];
int dir[11][MAXN],fr[11];
inline void era(int u,int mid,int v){
F(i,0,10) if(dir[i][mid]*dir[u][mid]==-1){
if(dir[u][mid]==1) del[u][i].emplace(val[u][mid]+val[i][mid],mid);
else del[i][u].emplace(val[u][mid]+val[i][mid],mid);
}
F(i,0,10) if(i!=u&&dir[i][mid]*dir[v][mid]==-1){
if(dir[v][mid]==-1) del[i][v].emplace(val[v][mid]+val[i][mid],mid);
else del[v][i].emplace(val[v][mid]+val[i][mid],mid);
}
}
inline void add(int u,int mid,int v){
F(i,0,10) if(dir[i][mid]*dir[v][mid]==-1){
if(dir[v][mid]==1) q[v][i].emplace(val[v][mid]+val[i][mid],mid);
else q[i][v].emplace(val[v][mid]+val[i][mid],mid);
}
F(i,0,10) if(i!=v&&dir[i][mid]*dir[u][mid]==-1){
if(dir[u][mid]==-1) q[i][u].emplace(val[u][mid]+val[i][mid],mid);
else q[u][i].emplace(val[u][mid]+val[i][mid],mid);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
F(i,1,n) cin>>px[i]>>py[i];
px[n+1]=px[1],py[n+1]=py[1];
F(i,1,m) cin>>x[i]>>y[i]>>v[i];
F(i,1,m){
val[0][i]=v[i],dir[0][i]=1;
val[10][i]=-INF;
F(j,1,9){
dir[j][i]=-1;
switch((j-1)/3){
case 0:{val[j][i]-=2*x[i];break;}
case 2:{val[j][i]+=2*x[i];break;}
}
switch(j%3){
case 1:{val[j][i]-=2*y[i];break;}
case 0:{val[j][i]+=2*y[i];break;}
}
}
}
F(i,1,n){
val[0][i+m]=-INF;
val[10][i+m]=-abs(px[i]-px[i+1])-abs(py[i]-py[i+1]),dir[10][i+m]=-1;
F(j,1,9){
dir[j][i+m]=1;
switch((j-1)/3){
case 0:{val[j][i+m]+=px[i]+px[i+1];break;}
case 1:{val[j][i+m]+=abs(px[i]-px[i+1]);break;}
case 2:{val[j][i+m]+=-px[i]-px[i+1];break;}
}
switch(j%3){
case 1:{val[j][i+m]+=py[i]+py[i+1];break;}
case 2:{val[j][i+m]+=abs(py[i]-py[i+1]);break;}
case 0:{val[j][i+m]+=-py[i]-py[i+1];break;}
}
}
}
F(i,1,m) F(j,1,9) q[0][j].emplace(val[0][i]+val[j][i],i);
F(i,1,n) F(j,1,9) q[j][10].emplace(val[10][i+m]+val[j][i+m],i+m);
F(i,1,n) ans+=abs(px[i]-px[i+1])+abs(py[i]-py[i+1]);
F(k,1,n){
F(i,0,10) F(j,0,10){
while(!q[i][j].empty()&&!del[i][j].empty()&&q[i][j].top()==del[i][j].top()) q[i][j].pop(),del[i][j].pop();
if(q[i][j].empty()) dis[i][j]=-INF;
else dis[i][j]=q[i][j].top().fi;
}
memset(dep,-0x3f,sizeof(dep));
static bool isin[11];
queue<int>qu;
qu.push(0);
dep[0]=0,isin[0]=1;
while(!qu.empty()){
int now=qu.front();
qu.pop();isin[now]=0;
F(i,0,10) if(dep[i]<dep[now]+dis[now][i]){
dep[i]=dep[now]+dis[now][i],fr[i]=now;
if(!isin[i]) isin[i]=1,qu.push(i);
}
}
ans+=dep[10];
cout<<ans<<" ";
int now=10,tp=0;
static int u[11],v[11],mid[11];
while(now){
int i=fr[now],t=q[i][now].top().se;
era(i,t,now);
++tp,u[tp]=i,mid[tp]=t,v[tp]=now;
now=i;
}
R(i,tp,1) dir[u[i]][mid[i]]*=-1,dir[v[i]][mid[i]]*=-1,val[u[i]][mid[i]]*=-1,val[v[i]][mid[i]]*=-1;
while(tp) add(u[tp],mid[tp],v[tp]),--tp;
}
return 0;
}
Day 110
还有几道难炸了的集训队互测,先放一道没有炸的。
2025/05/27 [集训队互测 2024] 生命的循环
题意
给定一张
思路
对于强连通的情况,周期显然为所有环环长的
对于其余情况,首先先对强连通分量缩点,把第
我们预处理
然后再预处理
全部预处理出来之后图论的部分就结束了。把
如果不互质就可以用这个题的 Trick。我们枚举长度模
最后就是怎么求并起来之后的周期的问题了。你发现并不好求,所以取反变成交,相当于是一个和上面说的那道题类似的同余方程组,用相同的方法做就行。
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define ll long long
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define fi first
#define se second
using namespace std;
const int MAXN=5001,MAXM=252001,M=2520;
int n,m,type,scc[MAXN],nn,per[MAXN],deg[MAXN];
vector<pair<int,int> >gs[MAXN];
bool exi[MAXN][101][101],s[MAXN][MAXM];
int bor[MAXM];
namespace SCC{
vector<pair<int,int> >g[MAXN],gt[MAXN];
int stk[MAXN],tp,dis[MAXN];
bool vis[MAXN];
void dfs1(int now){vis[now]=1;for(auto i:g[now])if(!vis[i.fi])dfs1(i.fi);stk[++tp]=now;}
void dfs2(int now,int id){scc[now]=id;for(auto i:gt[now])if(!scc[i.fi])dfs2(i.fi,id);}
void dfs3(int now){vis[now]=1;for(auto i:g[now])if(!vis[i.fi]&&scc[i.fi]==scc[now])dis[i.fi]=dis[now]+i.se,dfs3(i.fi);};
inline void main(){
F(i,1,n) if(!vis[i]) dfs1(i);
memset(vis,0,n+1);
while(tp){
if(!scc[stk[tp]]) dfs2(stk[tp],++nn);
--tp;
}
F(i,1,n) if(!vis[i]) dfs3(i);
F(i,1,n) for(auto j:g[i]){
if(scc[i]==scc[j.fi]) per[scc[i]]=__gcd(per[scc[i]],abs(j.se-dis[j.fi]+dis[i]));
else gs[scc[i]].emplace_back(scc[j.fi],j.se-dis[j.fi]+dis[i]),++deg[scc[j.fi]];
}
}
}
inline bool isp(int x){for(int i=2;i*i<=x;++i)if(x%i==0)return 0;return 1;}
int id[101],cnt,len[26],e[101];
inline void solve(int rem){
bool avai[26][101]={};
F(i,2,100){
int qwq=__gcd(i,M);
if(qwq==i){
if(exi[scc[n]][i][rem%i]) return;
continue;
}
F(j,0,len[id[i/qwq]]-1) avai[id[i/qwq]][j]|=exi[scc[n]][i][(j*M+rem)%i];
}
F(i,1,25){
bool flag=0;
F(j,0,len[i]-1) if(!avai[i][j]) flag=1;
if(!flag) return;
}
F(i,1,25) F(j,0,len[i]-1) if(!avai[i][j]) s[i][j*M+rem]=1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>type;
F(i,1,m){
int u,v,w;
cin>>u>>v>>w;
SCC::g[u].emplace_back(v,w);SCC::gt[v].emplace_back(u,w);
}
SCC::main();
queue<int>q;
F(i,1,100) exi[scc[1]][i][0]=1;
q.push(scc[1]);
while(!q.empty()){
int now=q.front();q.pop();
for(auto i:gs[now]){
F(j,1,100) F(r,0,j-1) if(exi[now][j][r]) exi[i.fi][j][((r+i.se)%j+j)%j]=1;
!(--deg[i.fi])&&(q.push(i.fi),1);
}
}
F(i,1,nn) for(auto j:gs[i]) ++deg[j.fi];
F(i,1,nn) F(j,1,100) if(j!=per[i]) F(k,0,j-1) exi[i][j][k]=0;
q.push(scc[1]);
while(!q.empty()){
int now=q.front();q.pop();
for(auto i:gs[now]){
F(j,1,100) F(r,0,j-1) if(exi[now][j][r]){
int qwq=__gcd(j,per[i.fi]);
exi[i.fi][qwq][((r+i.se)%qwq+qwq)%qwq]=1;
}
!(--deg[i.fi])&&(q.push(i.fi),1);
}
}
F(i,2,100) if(isp(i)){
int qwq=1;
++cnt;
while(qwq*i<=100) qwq*=i,id[qwq]=cnt;
len[id[qwq]]=qwq;
}
F(rem,0,M-1) solve(rem);
F(t,1,25){
static bool ch[MAXM];
int qwq=len[t]*M,qaq=qwq;
F(j,1,qwq) ch[j]=s[t][j-1];
for(int i(2),j(0);i<=qwq;++i){
while(j&&ch[i]!=ch[j+1]) j=bor[j];
if(ch[i]==ch[j+1]) ++j;
bor[i]=j;
}
if(qwq%(qwq-bor[qwq])==0) qaq=qwq-bor[qwq];
for(int j=2;j*j<=qaq;++j) if(qaq%j==0){
int ee=0;
while(qaq%j==0) qaq/=j,++ee;
e[j]=max(e[j],ee);
}
if(qaq!=1) e[qaq]=max(e[qaq],1);
}
ll ans=1;
F(i,2,100) while(e[i]) ans=ans*i%1000000009,--e[i];
cout<<ans;
return 0;
}
后记
最近有很多好题啊,慢慢往蜜月日记里面放吧。