用 LCT 维护操作, a c c e s s access access 的贡献是个子树加
#include
#define cs const
#define pb push_back
using namespace std;
typedef long long ll;
namespace IO{
cs int Rlen=1<<22|1;
inline char gc(){
static char buf[Rlen],*p1,*p2;
(p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin));
return p1==p2?EOF:*p1++;
} int read(){
int x=0; char c=gc(); bool f=false;
while(!isdigit(c))f=c=='-',c=gc();
while(isdigit(c))x=(((x<<2)+x)<<1)+(c^48),c=gc();
return f?-x:x;
} int opt(){
char c=gc(); while(isspace(c))c=gc();
c=gc(), c=gc(); return c=='L'?0:(c=='C'?1:2);
}
} using namespace IO;
cs int N = 1e5 + 50;
int n, m, rt;
vector<int> G[N];
int in[N], out[N], pt[N], sz[N], dfn;
int fa[N][20], lg[N], dep[N];
namespace SGT{
#define mid ((l+r)>>1)
cs int N = ::N << 2;
ll sm[N], t[N]; ll len[N];
void up(int x){ sm[x] = sm[x<<1] + sm[x<<1|1]; }
void build(int x, int l, int r){
len[x] = r - l + 1;
if(l == r) return sm[x] = dep[pt[l]], void();
build(x<<1,l,mid), build(x<<1|1,mid+1,r), up(x);
}
void put(int x, int v){ sm[x] += 1ll * len[x] * v, t[x] += v; }
void down(int x){ if(t[x])put(x<<1,t[x]),put(x<<1|1,t[x]),t[x]=0; }
void mdf(int x, int l, int r, int L, int R, int v){
if(L<=l&&r<=R) return put(x,v), void(); down(x);
if(L<=mid) mdf(x<<1,l,mid,L,R,v);
if(R>mid) mdf(x<<1|1,mid+1,r,L,R,v); up(x);
}
ll ask(int x, int l, int r, int L, int R){
if(L<=l&&r<=R) return sm[x]; down(x); ll ans = 0;
if(L<=mid) ans += ask(x<<1,l,mid,L,R);
if(R>mid) ans += ask(x<<1|1,mid+1,r,L,R); return ans;
}
}
int jmp(int x, int y){
for(int i=lg[dep[x]-dep[y]];~i;i--)
if(dep[fa[x][i]] > dep[y]) x = fa[x][i];
return x;
}
void modify(int x, int v){
if(!x) return;
if(x == rt) return SGT :: put(1,v), void();
if(in[x] <= in[rt] && in[rt] <= out[x]){
int t = jmp(rt, x);
SGT :: put(1,v), SGT :: mdf(1,1,n,in[t],out[t],-v);
} else return SGT :: mdf(1,1,n,in[x],out[x],v), void();
}
namespace LCT{
int ch[N][2], fa[N], lp[N], rp[N]; bool rv[N];
void up(int x){
rp[x] = ch[x][1] ? rp[ch[x][1]] : x;
lp[x] = ch[x][0] ? lp[ch[x][0]] : x;
}
int get(int x){ return ch[fa[x]][1]==x; }
bool isr(int x){ return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x; }
void put(int x){
if(!x) return;
swap(ch[x][0], ch[x][1]);
swap(lp[x], rp[x]), rv[x] ^= 1;
} void down(int x){ if(rv[x])put(ch[x][0]),put(ch[x][1]),rv[x]=0; }
void path(int x){ if(!isr(x)) path(fa[x]); down(x); }
void rot(int x){
int y=fa[x], z=fa[y], k=get(x);
if(!isr(y))ch[z][get(y)]=x; fa[x]=z;
ch[y][k]=ch[x][k^1], fa[ch[x][k^1]]=y;
ch[x][k^1] = y, fa[y] = x, up(y), up(x);
}
void spl(int x){
path(x); while(!isr(x)){
int y = fa[x]; if(!isr(y))
rot(get(x) ^ get(y) ? x : y); rot(x);
}
} void acs(int x){
for(int y=0;x;y=x,x=fa[x])
spl(x), modify(lp[ch[x][1]], 1),
modify(lp[ch[x][1] = y], -1), up(x);
} void mkrt(int x){
if(x == rt) return;
acs(x), spl(x), put(x), rt = x;
}
}
void pre_dfs(int u, int f){
pt[in[u] = ++dfn] = u, sz[u] = 1;
fa[u][0] = LCT :: fa[u] = f;
LCT :: rp[u] = LCT :: lp[u] = u;
dep[u] = dep[f] + 1;
for(int i=1; i<=lg[dep[u]]; i++)
fa[u][i] = fa[fa[u][i-1]][i-1];
for(int v : G[u]) if(v != f)
pre_dfs(v,u), sz[u] += sz[v]; out[u] = dfn;
}
double query(int x){
if(x == rt) return 1.0 * SGT :: sm[1] / n;
if(in[x] <= in[rt] && in[rt] <= out[x]){
int t = jmp(rt, x), z = n - sz[t];
ll o = SGT :: sm[1] - SGT :: ask(1,1,n,in[t],out[t]);
return 1.0 * o / z;
} return 1.0 * SGT :: ask(1,1,n,in[x],out[x]) / sz[x];
}
int main(){
#ifdef FSYolanda
freopen("1.in","r",stdin);
#endif
n=read(), m=read();
for(int i=2; i<=n; i++) lg[i]=lg[i>>1]+1;
for(int i=1,u,v;i<n;i++)
u=read(),v=read(),G[u].pb(v),G[v].pb(u);
pre_dfs(rt = 1,0), SGT::build(1,1,n);
while(m--){
int op = opt();
if(op == 0) LCT :: acs(read());
if(op == 1) LCT :: mkrt(read());
if(op == 2) printf("%.10lf\n", query(read()));
} return 0;
}
容斥,写成 E G F EGF EGF 的形式就是
a n s k = [ x n ] n ! ∑ i ≥ 1 ( x k k ) i 1 i ! ( − 1 ) i exp ∑ j ≥ 2 x j j ans_k=[x^n]n!\sum_{i\ge 1}(\frac{x^k}{k})^i\frac{1}{i!}(-1)^i\exp \sum_{j\ge 2}\frac{x^j}{j} ansk=[xn]n!i≥1∑(kxk)ii!1(−1)iexpj≥2∑jxj
注意到后面的本质是个错排, O ( n ) O(n) O(n) 递推一下即可
#include
#define cs const
#define pb push_back
using namespace std;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a,b); }
void Mul(int &a, int b){ a = mul(a,b); }
void Dec(int &a, int b){ a = dec(a,b); }
cs int N = 5e5 + 50;
int fc[N], ifc[N], n, d[N], iv[N];
int main(){
iv[1] = fc[0] = fc[1] = ifc[0] = ifc[1] = 1;
cin >> n;
for(int i=2; i<=n; i++)
iv[i] = mul(Mod-Mod/i, iv[Mod%i]);
for(int i=2; i<=n; i++)
fc[i] = mul(fc[i-1],i),
ifc[i] = mul(ifc[i-1],iv[i]);
d[0] = d[2] = 1;
for(int i=3; i<=n; i++)
d[i] = mul(add(d[i-2],d[i-1]),i-1);
for(int i=2; i<=n; i++)
Mul(d[i], ifc[i]);
int ans = 0;
for(int k=2; k<=n; k++)
for(int t=1,mt=iv[k],c; t*k<=n; t++){
c = mul(ifc[t],mt);
Mul(c, d[n-t*k]);
Add(ans, t & 1 ? c : dec(0,c));
Mul(mt, iv[k]);
} cout << mul(ans, fc[n]);
return 0;
}
还原出序列
注意到独立集的限制是选出一个递增序列
覆盖集的限制是每个点都存在一个逆序点
这等价于上升序列的两个中间没有夹着的点, O ( n 2 ) d p O(n^2)\ dp O(n2) dp
#include
#define cs const
#define pb push_back
using namespace std;
cs int Mod = 1e9 + 7;
void Add(int &a, int b){ a = a + b >= Mod ? a + b - Mod : a + b; }
cs int N = 1e3 + 50;
int n, m, dp[N], a[N];
int main(){
#ifdef FSYolanda
freopen("1.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
if(u<v) swap(u,v); ++a[u];
} set<int> S;
for(int i=1; i<=n; i++) S.insert(i);
for(int i=n; i>=1; i--){
auto t=S.end();--t;
while(a[i])--t,--a[i]; a[i]=*t;
S.erase(t);
}
for(int i=1,mn=1e9; i<=n; i++){
dp[i]=a[i]<mn; mn=min(mn,a[i]);
for(int j=i-1,t=0; j>=1; j--)
if(a[j]<a[i] && a[j]>t)
Add(dp[i],dp[j]), t=a[j];
} int ans=0;
for(int i=n,mx=0;i>=1;i--)
if(a[i]>mx)mx=a[i],Add(ans,dp[i]);
cout << ans; return 0;
}