• [CLYZ2017]day1


    区间

    image

    Solution

    30分

    暴力建边判可行.

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 1005
    using namespace std;
    struct graph{
    	int nxt,to;
    }e[N*N];
    struct inter{
    	int l,r;
    }a[N];
    int g[N],n,m,cnt;
    bool v[N];
    queue<int> q;
    inline void addedge(int x,int y){
    	e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
    }
    inline bool bfs(int u,int t){
    	memset(v,0,sizeof(v));
    	while(!q.empty()) q.pop();
    	q.push(u);v[u]=true;
    	while(!q.empty()){
    		u=q.front();q.pop();
    		for(int i=g[u];i;i=e[i].nxt){
    			if(e[i].to==t) 
    				return true;
    			if(!v[e[i].to]){
    				v[e[i].to]=true;
    				q.push(e[i].to);
    			}
    		}
    	}
    	return false;
    }
    inline void Aireen(){
    	scanf("%d",&n);
    	for(int i=1,ty,j,k;i<=n;++i){
    		scanf("%d",&ty);
    		if(ty&1){
    			++m;scanf("%d%d",&a[m].l,&a[m].r);
    			for(int j=1;j<m;++j){
    				if((a[j].l<a[m].l&&a[m].l<a[j].r)\
    				  ||(a[j].l<a[m].r&&a[m].r<a[j].r))
    					addedge(m,j);
    				if((a[m].l<a[j].l&&a[j].l<a[m].r)\
    				  ||(a[m].l<a[j].r&&a[j].r<a[m].r))
    					addedge(j,m);
    			}	
    		}
    		else{
    			scanf("%d%d",&j,&k);
    			if(bfs(j,k)) puts("YES");
    			else puts("NO");
    		}
    	}
    }
    int main(){
    	freopen("interval.in","r",stdin);
    	freopen("interval.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    100分

    线段树+\(vector\).
    互通的区间可以合并.
    剩下不互通的区间是棵森林.
    因为序列长度具有单调性,所以只需判左右端点属于哪些区间即可判互通.

    调不出来

    置换

    image

    Solution

    100分

    \(i->P_i\)建图,每个点顺序走两条边即可得到\(Q\).
    \(i->Q_i\)建图,判断现在的图是否能由上面那种方式得到.
    大小为奇数的环合法,大小为偶数的环需大小相等的两两合并.

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 1000005
    using namespace std;
    int a[N],f[N],g[N],fr[N],to[N],nxt[N],siz[N],ans[N],n;
    inline int read(){
    	int ret=0;char c=getchar();
    	while(!isdigit(c))
    		c=getchar();
    	while(isdigit(c)){
    		ret=(ret<<1)+(ret<<3)+c-'0';
    		c=getchar();
    	}
    	return ret;
    }
    inline void Aireen(){
    	n=read();
    	for(int i=1;i<=n;++i){
    		nxt[i]=read();
    		if(nxt[i]>n||nxt[i]<=0){
    			puts("-1");return;
    		}
    	}
    	for(int i=1;i<=n;++i){
    		++fr[i];++to[nxt[i]];
    	}
    	for(int i=1;i<=n;++i)
    		if(fr[i]!=1||to[i]!=1){
    			puts("-1");return;
    		}
    	memset(fr,0,sizeof(fr));
    	for(int i=1;i<=n;++i)
    		if(!f[i]){
    			for(int j=i;!f[j];j=nxt[j]){
    				f[j]=i;++siz[i];
    			}
    			if(!(siz[i]&1)){
    				if(fr[siz[i]]){
    					g[i]=fr[siz[i]];
    					fr[siz[i]]=0;
    				}
    				else fr[siz[i]]=i;
    			}
    		}
    	for(int i=1;i<=n;++i)
    		if(fr[siz[i]]){
    			puts("-1");return;
    		}
    	for(int i=1,k,l;i<=n;++i)
    		if(siz[i]&1){
    			a[1]=i;k=3;
    			for(int j=nxt[i];j!=i;j=nxt[j]){
    				if(k>siz[i]) k-=siz[i];
    				a[k]=j;k+=2;
    			}
    			for(int j=1;j<siz[i];++j)
    				ans[a[j]]=a[j+1];
    			ans[a[siz[i]]]=a[1];
    		}
    		else if(g[i]){
    			a[1]=i;k=3;l=siz[i]<<1;
    			for(int j=nxt[i];j!=i;j=nxt[j]){
    				a[k]=j;k+=2;
    			}
    			a[2]=g[i];k=4;
    			for(int j=nxt[g[i]];j!=g[i];j=nxt[j]){
    				a[k]=j;k+=2;
    			}
    			for(int j=1;j<l;++j)
    				ans[a[j]]=a[j+1];
    			ans[a[l]]=a[1];
    		}
    	for(int i=1;i<=n;++i)
    		printf("%d ",ans[i]);
    	printf("\n"); 
    }
    int main(){
    	freopen("permutation.in","r",stdin);
    	freopen("permutation.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    image

    Solution

    prufer数列

    \(prufer\)数列是无根树的一种数列一种

    • 生成\(prufer\)序列的方法:
      迭代删点,直到原图仅剩两个点.
      对于一棵顶点已经经过编号的树\(T\),顶点的编号为\(\{1,2,\dots,n\}\).
      每一步,移去所有叶子节点中标号最小的顶点和相连的边,并把与它相邻的点的编号加入\(prufer\)序列中.
      重复以上步骤直到原图仅剩\(2\)个顶点.

    • \(prufer\)数列转化成树的方法
      \(\{a_1,a_2,\dots,a_{n-2}\}\)为一棵有\(n\)个节点的树的\(prufer\)序列.
      另建一个集合\(G\)含有元素\(\{1,\dots,n\}\),找出集合中最小的未在\(prufer\)序列中出现过的数,将该点与\(prufer\)序列中首项连一条边,并将该点和\(prufer\)序列首项删除,重复操作\(n-2\)次,将集合中剩余的两个点之间连边即可.

    • 性质
      一个度数为\(d\)的点在\(prufer\)数列中只会出现\(d-1\)次.

    100分

    这题可以利用\(prufer\)数列的性质.
    一个大小为\(siz\)的树,每个点的度数为\(d_i\),
    则带编号的生成树计数为:\(\large\frac{siz!}{\prod{d_i!}}\),类似组合数(组合数计数也可以).
    因为过程中不知道\(siz\),所以先不\(\times{siz!}\).
    \(f[i][j][k]\)表示前\(i\)个点中,选\(j\)个,\(prufer\)数列长度为\(k\)的方案数.
    \(f[i][j][k]=f[i-1][j][k]+\sum_{l=0}^{a_i-1}f[i][j-1][k-l]\times\frac{1}{l!}(\)\(1<i\)度数\(\leq{a_i})\)
    \(ans=\sum_{i=1}^{i=n}{f[n][i][i-2]}\times{i!}\)

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 105
    #define M 1004535809ll
    using namespace std;
    typedef long long ll;
    int a[N],n;
    ll f[N][N][N],rev[N],fac[N];
    inline ll po(ll x,ll k){
    	ll ret=1ll;
    	while(k){
    		if(k&1ll) ret=ret*x%M;
    		x=x*x%M;k>>=1;
    	}
    	return ret;
    }
    inline void Aireen(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	fac[0]=fac[1]=1ll;
    	for(int i=2;i<=n;++i)
    		fac[i]=fac[i-1]*(ll)(i)%M;
    	rev[n]=po(fac[n],M-2ll);
    	rev[0]=rev[1]=1ll;
    	for(int i=n-1;i>1;--i)
    		rev[i]=rev[i+1]*(ll)(i+1)%M;
    	f[0][0][0]=1ll;
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<=i;++j)
    			for(int k=0;k<=n-2;++k){
    				f[i][j][k]=f[i-1][j][k];
    				if(j) for(int l=min(a[i]-1,k);l>=0;--l){
    					f[i][j][k]=f[i][j][k]+f[i-1][j-1][k-l]*rev[l];
    					if(f[i][j][k]>M) f[i][j][k]%=M; 
    				}
    			}
    	printf("%d ",n);
    	for(int i=2;i<=n;++i)
    		printf("%lld ",f[n][i][i-2]*fac[i-2]%M);
    	printf("\n");
    }
    int main(){
    	freopen("tree.in","r",stdin);
    	freopen("tree.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    回溯法---哈密顿回路(5)
    回溯法---n皇后问题(4)
    回溯法---n-着色问题(3)
    回溯法--算法框架(2)
    创建二叉树的所有深度上的节点链表
    笔试
    笔试 (2)
    LeetCode278-第一个错误的版本(二分查找)
    LeetCode46-全排列(递归)
    LeetCode258-各位相加(猜想公式)
  • 原文地址:https://www.cnblogs.com/AireenYe/p/CLYZ2017day1.html
Copyright © 2020-2023  润新知