A.神奇纸牌
比较好像的题,考虑一个数字一个数字的加,这样就变成了n个点,每个点四个01属性,有相同1属性就相连,最后是个连通图就行。
点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;
const int maxn=17;
int V=15,Mod;
int n;
int pw(int x,int p){int res=1,base=x;while(p){if(p&1)res=1LL*res*base%Mod;base=1LL*base*base%Mod;p>>=1;}return res;}
int Inv(int x){return pw(x,Mod-2);}
int g[maxn+1];
int Calc(int x){
int res=0;
if(x>n)return 0;
Rep(i,0,x)
if((x-i)&1)res=(res-pw(i,n))%Mod;
else res=(res+pw(i,n))%Mod;
return res;
}
int tmp[maxn],pre[maxn],suf[maxn];
int fa[maxn];
int Find(int x){ return fa[x]==x ? x : fa[x]=Find(fa[x]); }
bool Check(int x){
Rep(i,0,V)fa[i]=i;
Rep(i,0,V)if((x>>i)&1)tmp[i]=i;else tmp[i]=0;
Rep(i,0,V)Rep(j,i+1,V)
if(tmp[i]&tmp[j])fa[Find(tmp[i])]=fa[Find(tmp[j])];
set<int>s;
Rep(i,0,V)if(tmp[i])
s.insert(Find(tmp[i]));
return s.size()==1;
}
int C[maxn<<1][maxn<<1];
void Init(){
C[0][0]=1;Rep(i,1,16){ C[i][0]=1;Rep(j,1,i)C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod; }
for(int i=1;i<=16;++i)g[i]=pw(i,n);
for(int i=2;i<=16;++i){
for(int j=1;j<i;++j)
g[i]=(g[i]-1LL*C[i][j]*g[j])%Mod;
}
}
void solve(){
cin>>n>>Mod;int ans=0;
//for(int i=1;i<=16;++i)g[i]=Calc(i),cerr<<g[i]<<"\n";
//g[2]=2;
Init();//Rep(i,1,16)cerr<<g[i]<<"\n";
for(int i=1;i<(1<<16);++i)if(Check(i))
ans=(ans+g[__builtin_popcount(i)])%Mod;
ans=(ans+Mod)%Mod;
cout<<(ans+1)%Mod<<"\n";
}
#undef int
int main (){ fre(uno);ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }
对于此题数据范围一种复杂度比较优的做法是直接枚举最终出现的属性组合的集合,判断是否达到联通,若联通则就是比较简单的有标号小球分组,就是\([ \frac{x^n}{n!}](e^x-1)^c\),暴力二项式就完了,组合意义容斥也ok。
B.凌乱平衡树
首先一个技巧是\(\sum dep = \sum siz\)。
发现merge操作只与左树右链和右树左链有关,两边的siz形成两个单调递减的序列,贡献过程相当于维护双指针,根据题意移动一侧,并将另一侧当前位置的权加一次贡献,那么就可以用权值线段树求出每个数的贡献。rotate操作如果影响到了右/左链,那么就是在序列上添一个数或者删一个数,更新下加/删掉的数贡献即可,注意右对左有贡献,左对右也有贡献。
点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;
const int maxn=3e5+10;
ll ans;
int n,q;
struct Seg{
int tr[maxn<<2];
void Modify(int rt,int l,int r,int x,int w){
tr[rt]+=w;if(l==r)return;
int mid=(l+r)>>1;
if(x<=mid)Modify(rt<<1,l,mid,x,w);
else Modify(rt<<1|1,mid+1,r,x,w);
}
int Siz(int rt,int l,int r,int s,int t){
if(s<=l && t>=r)return tr[rt];
int mid=(l+r)>>1,res=0;
if(s<=mid)res=Siz(rt<<1,l,mid,s,t);
if(t>mid)res+=Siz(rt<<1|1,mid+1,r,s,t);
return res;
}
int Find(int rt,int l,int r,int x){
if(x<l || !tr[rt])return 0;
if(l==r)return l;
int mid=(l+r)>>1;
if(x<=mid)return Find(rt<<1,l,mid,x);
int tmp=Find(rt<<1|1,mid+1,r,x);
return tmp ? tmp : Find(rt<<1,l,mid,x);
}
}A,B;
struct Treap{
#define lch V[p].son[0]
#define rch V[p].son[1]
struct Ver{ int son[2],fa,siz; }V[maxn];
int root,n;
int Son(int p){ return(p==V[V[p].fa].son[1]); }
void Pushup(int p){ V[p].siz=V[lch].siz+V[rch].siz+1; }
void Dfs(int p){ if(!p)return; V[p].siz=1;Dfs(lch),Dfs(rch),Pushup(p);ans+=V[p].siz; }
void Init(){
Rep(i,1,n){
int x,y;cin>>x>>y;
V[i].son[0]=x,V[i].son[1]=y;
if(x)V[x].fa=i;if(y)V[y].fa=i;
}
Rep(i,1,n)if(!V[i].fa)root=i;
Dfs(root);
}
}L,R;
bool Bl[maxn],Br[maxn];
#define V L.V
void RotateL(int p){
int f=V[p].fa,s=L.Son(p),lf=V[f].fa,ls=L.Son(f);
ans-=V[p].siz+V[f].siz;
if(s){
V[f].son[1]=V[p].son[0];
if(V[p].son[0])V[V[p].son[0]].fa=f;
V[p].son[0]=f,V[f].fa=p,V[p].fa=lf;
if(Bl[f]){
int num=V[p].siz;
int ax=V[V[p].son[1]].siz,ay=V[f].siz,bx=B.Find(1,1,n,num);
ans-=1LL*(num-ax)*B.Siz(1,1,n,num+1,ay)+bx;
A.Modify(1,1,n,num,-1),Bl[f]=false;
}
L.Pushup(f),L.Pushup(p);
}else{
V[f].son[0]=V[p].son[1];
if(V[p].son[1])V[V[p].son[1]].fa=f;
V[p].son[1]=f,V[f].fa=p,V[p].fa=lf;
L.Pushup(f),L.Pushup(p);
if(Bl[f]){
int num=V[f].siz;
int ax=V[V[f].son[1]].siz,ay=V[p].siz,bx=B.Find(1,1,n,num);
ans+=1LL*(num-ax)*B.Siz(1,1,n,num+1,ay)+bx;
A.Modify(1,1,n,num,1),Bl[p]=true;
}
}
if(lf)V[lf].son[ls]=p;
if(L.root==f)L.root=p;
ans+=V[p].siz+V[f].siz;
}
#undef V
#define V R.V
void RotateR(int p){
int f=V[p].fa,s=R.Son(p),lf=V[f].fa,ls=R.Son(f);
ans-=V[p].siz+V[f].siz;
if(s){
V[f].son[1]=V[p].son[0];
if(V[p].son[0])V[V[p].son[0]].fa=f;
V[p].son[0]=f,V[f].fa=p,V[p].fa=lf;
R.Pushup(f),R.Pushup(p);
if(Br[f]){
int num=V[f].siz;
int ax=V[V[f].son[0]].siz,ay=V[p].siz,bx=A.Find(1,1,n,num-1);
ans+=1LL*(num-ax)*A.Siz(1,1,n,num,ay-1)+bx;
B.Modify(1,1,n,num,1),Br[p]=true;
}
}else{
V[f].son[0]=V[p].son[1];
if(V[p].son[1])V[V[p].son[1]].fa=f;
V[p].son[1]=f,V[f].fa=p,V[p].fa=lf;
if(Br[f]){
int num=V[p].siz;
int ax=V[V[p].son[0]].siz,ay=V[f].siz,bx=A.Find(1,1,n,num-1);
ans-=1LL*(num-ax)*A.Siz(1,1,n,num,ay-1)+bx;
B.Modify(1,1,n,num,-1),Br[f]=false;
}
R.Pushup(f),R.Pushup(p);
}
if(lf)V[lf].son[ls]=p;
if(R.root==f)R.root=p;
ans+=V[p].siz+V[f].siz;
}
#undef V
void solve(){
cin>>L.n>>R.n;n=max(L.n,R.n);
L.Init(),R.Init();
for(int p=L.root;p;p=L.V[p].son[1])A.Modify(1,1,n,L.V[p].siz,1),Bl[p]=true;
for(int p=R.root;p;p=R.V[p].son[0])B.Modify(1,1,n,R.V[p].siz,1),Br[p]=true;
for(int p=L.root;p;p=L.V[p].son[1])
ans+=1LL*L.V[p].siz*B.Siz(1,1,n,L.V[p].siz+1,p==L.root ? n : L.V[L.V[p].fa].siz);
for(int p=R.root;p;p=R.V[p].son[0])
ans+=1LL*R.V[p].siz*A.Siz(1,1,n,R.V[p].siz,p==R.root ? n : R.V[R.V[p].fa].siz-1);
cout<<ans<<"\n";
cin>>q;
while(q--){
int opt,x;cin>>opt>>x;
if(opt==1)RotateL(x);
else RotateR(x);
cout<<ans<<"\n";
}
}
#undef int
int main (){ fre(treap);ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }
C.打扫笛卡尔
套路的设\(f_i\)表示一棵大小为\(i\)的笛卡尔树的期望深度,枚举左右子树大小合并转移,每个点的贡献是\(P(x) \times dep_x\) 的形式,合并的时候深度加一,于是再维护一个\(g_i\)表示概率和,于是就可以转移了。简单化一化柿子发现最后全都合并同类项了,拆开组合数后是一个乘下降幂系数的前缀和,比对下系数发现每次就是再乘个\(i-1\),于是直接递推就行了。
点击查看代码
// ubsan: undefined
// accoders
#pragma GCC optimize(3)
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("-ffast-math")
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;
const int maxn=1e7+10;
int n,Mod;
ll f,g[maxn],h[maxn],preg[maxn],preh[maxn];
int fac[maxn],inv[maxn];
void solve(){
cin>>n>>Mod;
fac[0]=inv[0]=1;Rep(i,1,n)fac[i]=1LL*fac[i-1]*i%Mod;
f=1,g[1]=1,h[1]=2%Mod;
Rep(i,2,n){
preh[i-1]=1LL*preh[i-1]*(i-1)%Mod;
preg[i-1]=1LL*preg[i-1]*(i-1)%Mod;
f=(fac[i]+preh[i-1]+h[i-1])%Mod;
g[i]=(fac[i]+preg[i-1]+g[i-1])%Mod;
h[i]=(f+g[i])%Mod;
preh[i]=(preh[i-1]+h[i-1])%Mod;
preg[i]=(preg[i-1]+g[i-1])%Mod;
/*Rep(j,1,i-1)
f[i]=(f[i]+1LL*(f[j]+g[j])%Mod*inv[j]%Mod*fac[i-1]%Mod)%Mod,
g[i]=(g[i]+1LL*g[j]*inv[j]%Mod*fac[i-1]%Mod)%Mod;
f[i]=(f[i]+fac[i])%Mod;
g[i]=(g[i]+fac[i])%Mod;*/
// cerr<<f[i]<<" "<<g[i]<<"\n";
}
cout<<f<<"\n";
}
#undef int
int main (){ fre(cartesian);ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }