NOI 模拟 20/07/18

A

用 LCT 维护操作, a c c e s s access access 的贡献是个子树加

#include<bits/stdc++.h>
#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;
}

B

容斥,写成 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!i1(kxk)ii!1(1)iexpj2jxj
注意到后面的本质是个错排, O ( n ) O(n) O(n) 递推一下即可

#include<bits/stdc++.h>
#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;
}

C

还原出序列
注意到独立集的限制是选出一个递增序列
覆盖集的限制是每个点都存在一个逆序点
这等价于上升序列的两个中间没有夹着的点, O ( n 2 )   d p O(n^2)\ dp O(n2) dp

#include<bits/stdc++.h>
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值