8.25考试总结

8.25考试总结

find

由于某些不可告知的原因,暂时不公开

page

原题,搞一个堆来贪心的选,每次选择一个下一个离得最远的点扔出去

这样正确性显然

如果一个数在堆里,我们要把他的nxt修改一下

这个貌似用堆不太好维护

我们就考虑换成set,因为这个数一定是最小的

直接搞搞就可以了

//STL重度依赖症QAQ 
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<set>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 3e5 + 3;
int a[N],nxt[N];
int n,k;
int pos[N];
bool book[N];
int ans;

multiset < pii > s;
multiset < pii >::iterator it;
inline int read(){
	int v = 0,c = 1;char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-') c = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		v = v * 10 + ch - 48;
		ch = getchar();
	}
	return v * c;
}
int main(){
	freopen("page.in","r",stdin);
	freopen("page.out","w",stdout);
	n = read(),k = read();
	for(int i = 1;i <= n;++i) {
		a[i] = read();
		if(pos[a[i]]) nxt[pos[a[i]]] = i;
		pos[a[i]] = i;
	}
	for(int i = 1;i <= n;++i) if(!nxt[i]) nxt[i] = n + 1;
	for(int i = 1;i <= n;++i){
		if(book[a[i]]){
			it = s.begin();
			pii x = *it;
			s.erase(it);
			s.insert(mk(nxt[i],i));
			continue;
		}
		if(s.size() < k){
			ans++;
			book[a[i]] = 1;
			s.insert(mk(nxt[i],i));
			continue;
		}
		ans++;
		it = s.end();it--;
		pii x = *it;
		s.erase(it);
		s.insert(mk(nxt[i],i));
		book[a[x.se]] = 0;
		book[a[i]] = 1;
	}
	printf("%d
",ans);
	return 0;
}

graph

首先,模型转化,题目变成了

给你(n)个连通块,连通块内的点不能在连通块内的位置

求合法的排列个数

首先(m = 0)怎么做

所有的连通块的大小都是(1)的本质是错排

我们回想一下错排的容斥写法

[ans = sum_{i = 0}^n(-1)^ileft(egin{array}{l}{i} \ {n}end{array} ight)(n - i)! ]

强制(i)个点在不合法的位置上,其余的点随便排

接下来我们进行推广,当连通块大小不为(0)是,我们就不能这么算了

但我们还是考虑如何算出至少有(j)个点不合法的方案数

之后发现这个东西之和连通块的大小有关

我们设(g_{i,j})表示大小为(i)的连通块,选了(j)个点的方案数,很明显,这(j)个点都是不合法的

[g_{i,j} = left(egin{array}{l}{i} \ {j}end{array} ight) imesfrac{i!}{(i - j)!} ]

前面就是我们选出(j)个点,后面是乘法原理

第一个点有(i)种选择,第二个点有(i - 1)

所以就是(i imes(i - 1) imesdots imes(i - j + 1) = frac{i!}{(i - j)!})

那么我们设(f_{i,j})表示考虑了前(i)个连通块至少有(j)个点不合法的方案数

转移就是枚举这个连通块选了多少了不合法点

[f_{i,j }= sum_{k = 0}^{size_i}f_{i - 1,j - k} imes{g_{size_i,k}} ]

这样我们在像错排那样把答案容斥出来就好了

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 2003;
const LL mod = 1e9 + 7;
LL f[N][N];
LL g[N][N];
int n,m,tot;
int fa[N];
int size[N];
int be[N];
LL inv[N],fac[N];
inline int getf(int x){	
	return fa[x] == x ? x : fa[x] = getf(fa[x]);
}
inline int read(){
	int v = 0,c = 1;char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-') c = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		v = v * 10 + ch - 48;
		ch = getchar();
	}
	return v * c;
}
inline LL quick(LL x,LL y){
	LL res = 1;
	while(y){
		if(y & 1) res = res * x % mod;
		y >>= 1;
		x = x * x % mod;
	}
	return res;
} 
inline LL C(LL x,LL y){
	if(x < y) return 0;
	return fac[x] * inv[y] % mod * inv[x - y] % mod;
}
inline LL up(LL x){
	if(x >= mod) x -= mod;
	return x;
}
int main(){
	n = read(),m = read();
	fac[0] = inv[0] = 1;
	for(int i = 1;i <= n;++i){
		fac[i] = fac[i - 1] * i % mod; 
		inv[i] = quick(fac[i],mod - 2);	
	}
	for(int i = 1;i <= n;++i) fa[i] = i;
	for(int i = 1;i <= m;++i){
		int u = getf(read()),v = getf(read());	
		fa[u] = v;
	}
	for(int i = 1;i <= n;++i){
		int x = getf(i);
		be[i] = x;
	//	if(x == i) tot++;
		size[x]++;
	}
	for(int i = 1;i <= n;++i){
		if(size[i]) size[++tot] = size[i];	
	}
	for(int i = 0;i <= n;++i)
		for(int j = 0;j <= i;++j)
			g[i][j] = C(i,j) * fac[i] % mod * inv[i - j] % mod;
	f[0][0] = 1;
	//printf("%d ",tot);
	//for(int i = 1;i <= tot;++i) printf("%d ",size[i]);puts("");
	for(int i = 1;i <= tot;++i){
		for(int j = 0;j <= n;++j){
			for(int k = 0;k <= min(j,size[i]);++k){
				f[i][j] = up(f[i][j] + f[i - 1][j - k] * g[size[i]][k] % mod);
			}
		}	
	}
	LL ans = 0;
	for(int i = 0;i <= n;++i){
		if(i & 1) ans -= f[tot][i] * fac[n - i] % mod;
		else ans += f[tot][i] * fac[n - i] % mod;	
	}
	printf("%lld
",(ans % mod + mod) % mod);
	return 0;
}

原文地址:https://www.cnblogs.com/wyxdrqc/p/11409114.html