zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    002-数据库命名开发规范
    006-多线程-基础-同步解决 概述【ReentrantLock、Semaphore、CyclicBarrier、CountDownLatch】
    005-多线程-基础-sleep、join、yield、wait、notify、notifyAll、run、start、synchronized
    004-多线程-基础-同步问题引出、同步问题解决、死锁、生产者与消费者
    003-多线程-基础-其他【命名和取得、休眠、优先级、线程状态、线程中断】
    002-多线程-基础-实现方式【thread、runnable、callale、thread和runnable对比】
    001-多线程-基础-进程线程、线程状态、优先级、用户线程和守护线程
    001-Spring在代码中获取bean的几种方式
    004-mysql explain详解
    java-序列化-001-原生介绍
  • 原文地址:https://www.cnblogs.com/wyxdrqc/p/11409114.html
Copyright © 2011-2022 走看看