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\n",ans);
	return 0;
}

graph

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

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

求合法的排列个数

首先\(m = 0\)怎么做

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

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

\[ans = \sum_{i = 0}^n(-1)^i\left(\begin{array}{l}{i} \\ {n}\end{array}\right)(n - i)! \]

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

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

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

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

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

\[g_{i,j} = \left(\begin{array}{l}{i} \\ {j}\end{array}\right)\times\frac{i!}{(i - j)!} \]

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

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

所以就是\(i \times(i - 1)\times\dots\times(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}\times{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\n",(ans % mod + mod) % mod);
	return 0;
}

posted @ 2019-08-25 20:06  wyxdrqcccc  阅读(178)  评论(0编辑  收藏  举报