【考试总结】2022-06-05
求和
将 变成奇数并调整求和顺序,设
不难发现
据此可以得到顺推式
模数为奇数时可以直接 合并,模单个因子的组合数可以通过记录次数和剩余部分乘积来计算
模数含有质因子 时需要得到逆推式得到的
但是 大于 的指数时结果为 ,那么可以暴力计算每一项
Code Display
inline void exgcd(int a,int b,int &x,int &y){
if(!b) return x=1,y=0,void(); exgcd(b,a%b,x,y);
int k=x; x=y; y=k-(a/b)*x;
return ;
}
inline int Inv(int x,int mod){
int p,q;
exgcd(x,mod,p,q);
return (p%mod+mod)%mod;
}
int n,m,mod;
int a[100],M[100],t[100],pcnt;
const int N=1e6+100;
int binom[N],tim[N],pw[N],f[N];
inline int solve(int p,int k,int pk){
int inv=Inv(2,pk);
int cur=binom[0]=1,e=0;
for(int i=1;i<=m+1;++i){
int tmp=n+2-i;
while(tmp%p==0) ++e,tmp/=p;
cur=cur*tmp%pk;
tmp=i;
while(tmp%p==0) --e,tmp/=p;
cur=cur*Inv(tmp,pk)%pk;
if(e>=k) binom[i]=0;
else binom[i]=cur*pw[e]%pk;
}
f[0]=(n+1)/2;
for(int i=1;i<=m;++i) f[i]=inv*(binom[i+1]+pk-f[i-1])%pk;
int sum=0;
rep(i,0,m) if(!(i&1)) sum+=f[i];
return sum;
}
inline int solve_2(int k,int pk){
int cur=binom[0]=1,e=0;
for(int i=1;i<=m+k;++i){
int tmp=n+2-i;
while(!(tmp&1)) ++e,tmp/=2;
cur=cur*tmp%pk;
tmp=i;
while(!(tmp&1)) --e,tmp/=2;
cur=cur*Inv(tmp,pk)%pk;
if(e>=k) binom[i]=0;
else binom[i]=cur*pw[e]%pk;
}
int sum=0;
for(int i=0;i<=m;i+=2){
int f=0;
for(int j=0;j<k;++j){
if(j&1) f-=pw[j]*binom[i+j+2]%pk;
else f+=pw[j]*binom[i+j+2]%pk;
}
sum+=f%pk;
}
sum=(sum%pk+pk)%pk;
return sum;
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&mod);
if(mod==1) puts("0"),exit(0);
if(mod==2) pw[0]=1,printf("%lld\n",solve_2(1,2)),exit(0);
if(!(n&1)) ++n;
m=min(n,m);
int Mod=mod;
int ans=0;
for(int i=2;i*i<=Mod;++i) if(Mod%i==0){
int pk=1,k=0;
pw[k]=1;
while(Mod%i==0) pw[++k]=(pk*=i),Mod/=i;
if(i!=2) a[++pcnt]=solve(i,k,pk);
else a[++pcnt]=solve_2(k,pk);
M[pcnt]=mod/pk;
t[pcnt]=Inv(M[pcnt],pk);
}
if(Mod>1){
pw[0]=1;
pw[1]=Mod;
a[++pcnt]=solve(Mod,1,Mod);
M[pcnt]=mod/Mod;
t[pcnt]=Inv(M[pcnt],Mod);
}
for(int i=1;i<=pcnt;++i) ans=ans+t[i]*a[i]%mod*M[i]%mod;
cout<<ans%mod<<endl;
return 0;
}
农民
每个非根节点和其父亲的连边关系本质上是让子树中所有元素 或者 父亲点的权值
可以使用重链剖分来维护合法权值区间,单点修改就是单点修改
对于子树翻转左右儿子的操作,在线段树上维护反向关系支持交换即可
Code Display
const int N=1e5+10;
int n,Q,val[N],Ls[N],Rs[N];
int dfn[N],ord[N],son[N],top[N],siz[N],fa[N],dep[N],tim;
vector<int> G[N];
inline void dfs1(int x,int fat){
dep[x]=dep[fa[x]=fat]+(siz[x]=1);
for(auto t:G[x]) if(t!=fat){
dfs1(t,x); siz[x]+=siz[t];
if(siz[son[x]]<siz[t]) son[x]=t;
}
return ;
}
inline void dfs2(int x,int topf){
top[x]=topf; ord[dfn[x]=++tim]=x;
if(son[x]) dfs2(son[x],topf);
for(auto t:G[x]) if(!dfn[t]) dfs2(t,t);
}
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
const int inf=0x3f3f3f3f3f3f3f3f;
struct interval{
int l,r;
interval operator +(const interval &a)const{
return {max(l,a.l),min(r,a.r)};
}
}rng[N<<2][2];
int tag[N<<2];
inline void push_up(int p){
rng[p][0]=rng[ls][0]+rng[rs][0];
rng[p][1]=rng[ls][1]+rng[rs][1];
return ;
}
inline void push_tag(int p){swap(rng[p][0],rng[p][1]); tag[p]^=1;}
inline void push_down(int p){
if(tag[p]){
push_tag(ls); push_tag(rs);
tag[p]=0;
}
}
inline void build(int p,int l,int r){
if(l==r){
int x=ord[l];
if(Rs[fa[x]]==x){
rng[p][0]={val[fa[x]]+1,inf};
rng[p][1]={-inf,val[fa[x]]-1};
}else{
rng[p][0]={-inf,val[fa[x]]-1};
rng[p][1]={val[fa[x]]+1,inf};
}
return ;
}
int mid=(l+r)>>1;
build(p<<1,l,mid); build(p<<1|1,mid+1,r);
return push_up(p);
}
inline void push_rev(int st,int ed,int p=1,int l=2,int r=n){
if(st<=l&&r<=ed) return push_tag(p);
int mid=(l+r)>>1; push_down(p);
if(st<=mid) push_rev(st,ed,lson);
if(ed>mid) push_rev(st,ed,rson);
return push_up(p);
}
inline interval query(int st,int ed,int p=1,int l=2,int r=n){
if(st<=l&&r<=ed) return rng[p][0];
int mid=(l+r)>>1; push_down(p);
if(ed<=mid) return query(st,ed,lson);
if(st>mid) return query(st,ed,rson);
return query(st,ed,lson)+query(st,ed,rson);
}
inline void Modify(int pos,int v,int p=1,int l=2,int r=n){
if(l==r){
if(rng[p][0].l==-inf) rng[p][0].r=v-1;
else rng[p][0].l=v+1;
if(rng[p][1].l==-inf) rng[p][1].r=v-1;
else rng[p][1].l=v+1;
return ;
}
int mid=(l+r)>>1; push_down(p);
if(pos<=mid) Modify(pos,v,lson);
else Modify(pos,v,rson);
return push_up(p);
}
#undef ls
#undef rs
#undef lson
#undef rson
bool mark[N];
int rt;
signed main(){
n=read(); Q=read();
for(int i=1;i<=n;++i){
val[i]=read();
Ls[i]=read(); Rs[i]=read();
if(Ls[i]){
mark[Ls[i]]=1;
G[i].emplace_back(Ls[i]);
}
if(Rs[i]){
mark[Rs[i]]=1;
G[i].emplace_back(Rs[i]);
}
}
for(int i=1;i<=n;++i) if(!mark[i]){rt=i; break;}
dfs1(rt,0);
dfs2(rt,rt);
if(n>1) build(1,2,n);
while(Q--){
int opt=read();
if(opt==1){
int x=read(),v=read();
if(Ls[x]) Modify(dfn[Ls[x]],v);
if(Rs[x]) Modify(dfn[Rs[x]],v);
val[x]=v;
}else if(opt==2){
int x=read();
if(siz[x]>1) push_rev(dfn[x]+1,dfn[x]+siz[x]-1);
}else{
int x=read(),self=val[x];
interval ans={-inf,+inf};
while(top[x]!=rt){
ans=ans+query(dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(x!=rt) ans=ans+query(2,dfn[x]);
if(self>=ans.l&&self<=ans.r) puts("YES");
else puts("NO");
}
}
return 0;
}
仙人掌
建立圆方树,设 表示只考虑 点在圆方树的子树内边时 有 条出边的方案数
每个虚点需要合并环上的信息,设 为到了环上第 个点,环顶连出的第一条边的方向,上一条边的方向;转移需要根据每个环上点的 判断使用哪些 的和
对于实点可以将每个虚儿子的信息表示成 其中 表示环上有 条边连向环顶,使用分治乘法合并即可
Code Display
int n,m,nds;
int a[N],deg[N];
vector<int> g[N],G[N];
int low[N],dfn[N],tim,stk[N],top;
map<pair<int,int> ,int>mp;
inline void ins(int x,int t,int id){
if(x>t) swap(x,t);
mp[{x,t}]=id;
}
inline int query(int x,int t){
if(x>t) swap(x,t);
if(mp.count({x,t})) return mp[{x,t}];
return -1;
}
bool mark[N];
inline void tarjan(int x){
dfn[x]=low[x]=++tim; stk[++top]=x;
for(auto t:g[x]){
if(!dfn[t]){
tarjan(t); ckmin(low[x],low[t]);
if(low[t]>=dfn[x]){
++nds;
G[x].emplace_back(nds);
G[nds].emplace_back(x);
ins(x,t,nds);
do{
G[stk[top]].emplace_back(nds);
G[nds].emplace_back(stk[top]);
--top;
}while(stk[top+1]!=t);
}
}else{
int id=query(x,t);
if(id==-1) ckmin(low[x],dfn[t]);
else mark[id]=1;
}
}
}
inline vector<int> Mul(vector<int> a,vector<int> b){
int n=a.size(),m=b.size();
int lim=1; while(lim<=n+m) lim<<=1;
NTT(a,lim,1); NTT(b,lim,1);
vector<int> c(lim);
rep(i,0,lim-1) c[i]=mul(a[i],b[i]);
NTT(c,lim,-1);
c.resize(n+m-1);
return c;
}
vector<int> dp[N];
int f[N][3],ar[N][3];
inline vector<int> solve(int l,int r){
if(l==r) return {ar[l][0],ar[l][1],ar[l][2]};
int mid=(l+r)>>1;
return Mul(solve(l,mid),solve(mid+1,r));
}
int tmp[N][2][2];
// first edge direction
// current edge direction
inline void DP(int x,int fat){
for(auto t:G[x]) if(t!=fat) DP(t,x);
if(x<=n){
int m=0;
for(auto t:G[x]) if(t!=fat){
++m;
rep(i,0,2) ar[m][i]=f[t][i];
}
if(m) dp[x]=solve(1,m);
else dp[x]={1};
dp[x].resize(a[x]+1);
for(int i=0;i<=a[x]-2;++i) ckadd(f[x][2],dp[x][i]);
f[x][1]=add(f[x][2],dp[x][a[x]-1]);
f[x][0]=add(f[x][1],dp[x][a[x]]);
}else{
if(G[x].size()==2&&!mark[x]){
for(auto t:G[x]) if(t!=fat){
f[x][0]=f[t][1];
f[x][1]=f[t][0];
return ;
}
}
vector<int> son;
for(auto t:G[x]) if(t!=fat) son.emplace_back(t);
int num=son.size();
tmp[0][0][0]=f[son[0]][1];
tmp[0][0][1]=f[son[0]][2];
tmp[0][1][0]=f[son[0]][0];
tmp[0][1][1]=f[son[0]][1];
for(int i=1;i<num;++i){
tmp[i][0][0]=tmp[i][0][1]=tmp[i][1][0]=tmp[i][1][1]=0;
int t=son[i];
for(int s=0;s<=1;++s){
for(int c=0;c<=1;++c) if(tmp[i-1][s][c]){
ckadd(tmp[i][s][0],mul(f[t][!c],tmp[i-1][s][c]));
ckadd(tmp[i][s][1],mul(f[t][(!c)+1],tmp[i-1][s][c]));
}
}
}
f[x][2]=tmp[num-1][1][0];
f[x][1]=add(tmp[num-1][1][1],tmp[num-1][0][0]);
f[x][0]=tmp[num-1][0][1];
}
return ;
}
signed main(){
nds=n=read(); m=read();
for(int i=1;i<=m;++i){
int u=read(),v=read();
deg[u]++; deg[v]++;
g[u].emplace_back(v); g[v].emplace_back(u);
}
for(int i=1;i<=n;++i) a[i]=min(read(),2+deg[i]);
tarjan(1);
DP(1,0);
print(f[1][0]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律