zoukankan      html  css  js  c++  java
  • 省选测试7

    总结

    凭着第三题一个剪枝到了第二

    (T1) 基环树基本都能想到,大部分人都是实现出了问题

    (T2) (8 imes n^2) 能够 (2e4) 挺玄学的

    (T3) 加个剪枝 (n^22^n) 就过了

    A. 同桌的你

    分析

    因为每一个点只有一个出度所以最终一定会形成一个基环森林

    然后每一次找出环,把环断掉跑一个最大匹配就行了

    一种写法是只断一条边然后然后强制这条边选或者不选,还有一种写法是分别断一个环上的两条边后选一个最大的

    因为要在匹配数尽量大的前提下让异性配对的人尽量多

    所以可以把同性之间的边权设为 (1e6),异性之间的边权设为 (1e6+1)

    第一问答案就是最大匹配除以 (1e6),第二问的答案就是最大匹配对 (1e6) 取模

    输出方案的时候找一下最优决策点就行了

    代码

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    #include<cmath>
    #include<set>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=1e6+5;
    int t,n,a[maxn],x[maxn],h[maxn],tot=2,sta[maxn],jl1,jl2,tp;
    #define Max(a,b) (a>b?a:b)
    struct asd{
    	int to,nxt,val;
    }b[maxn<<1];
    void ad(rg int aa,rg int bb,rg int cc){
    	b[tot].to=bb;
    	b[tot].nxt=h[aa];
    	b[tot].val=cc;
    	h[aa]=tot++;
    }
    bool vis[maxn],ishuan,cut[maxn<<1];
    void dfs(rg int now,rg int lat,rg int pre){
    	vis[now]=1;
    	sta[++tp]=now;
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(u==lat) continue;
    		if(vis[u]){
    			if(!ishuan) jl1=i,jl2=pre,ishuan=1;
    		} else {
    			dfs(u,now,i);
    		}
    	}
    }
    long long f[maxn][2],ans,g[maxn][2];
    void dfs2(rg int now,rg int lat){
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(u==lat || cut[i]) continue;
    		dfs2(u,now);
    		f[now][0]+=Max(f[u][1],f[u][0]);
    	}
    	for(rg int i=h[now];i!=-1;i=b[i].nxt){
    		rg int u=b[i].to;
    		if(u==lat || cut[i]) continue;
    		f[now][1]=Max(f[now][1],f[now][0]-Max(f[u][0],f[u][1])+f[u][0]+b[i].val);
    	}
    }
    void pri(rg int now,rg int lat,rg int op){
    	if(op==0){
    		for(rg int i=h[now];i!=-1;i=b[i].nxt){
    			rg int u=b[i].to;
    			if(cut[i] || u==lat) continue;
    			pri(u,now,f[u][1]>f[u][0]);
    		}
    	} else {
    		rg int jud=0;
    		for(rg int i=h[now];i!=-1;i=b[i].nxt){
    			rg int u=b[i].to;
    			if(u==lat || cut[i]) continue;
    			if(!jud && f[now][1]==f[now][0]-Max(f[u][0],f[u][1])+f[u][0]+b[i].val){
    				printf("%d %d
    ",now,u);
    				jud=1;
    				pri(u,now,0);
    			} else {
    				pri(u,now,f[u][1]>f[u][0]);
    			}
    		}
    	}
    }
    int judrt[maxn];
    int main(){
    	t=read();
    	while(t--){
    		memset(h,-1,sizeof(h));
    		memset(f,0,sizeof(f));
    		memset(vis,0,sizeof(vis));
    		memset(cut,0,sizeof(cut));
    		ans=0,tot=2;
    		n=read();
    		for(rg int i=1;i<=n;i++){
    			vis[i]=0;
    			a[i]=read(),x[i]=read();
    		}
    		for(rg int i=1;i<=n;i++){
    			if(a[a[i]]!=i){
    				if(x[i]==x[a[i]]){
    					ad(i,a[i],1e6);
    					ad(a[i],i,1e6);
    				} else {
    					ad(i,a[i],1e6+1);
    					ad(a[i],i,1e6+1);
    				}
    			} else {
    				if(i<a[i]){
    					if(x[i]==x[a[i]]){
    						ad(i,a[i],1e6);
    						ad(a[i],i,1e6);
    					} else {
    						ad(i,a[i],1e6+1);
    						ad(a[i],i,1e6+1);
    					}
    				}
    			}
    		}
    		rg long long tmp=0;
    		for(rg int i=1;i<=n;i++){
    			if(!vis[i]){
    				ishuan=0,tp=0;
    				dfs(i,0,0);
    				if(ishuan){
    					tmp=0;
    					cut[jl1]=cut[jl1^1]=1;
    					dfs2(b[jl1].to,0);
    					tmp=Max(tmp,Max(f[b[jl1].to][0],f[b[jl1].to][1]));
    					cut[jl1]=cut[jl1^1]=0;
    					for(rg int j=1;j<=tp;j++){
    						g[sta[j]][0]=f[sta[j]][0],g[sta[j]][1]=f[sta[j]][1];
    						f[sta[j]][0]=f[sta[j]][1]=0;
    					}
    					cut[jl2]=cut[jl2^1]=1;
    					dfs2(b[jl2].to,0);
    					cut[jl2]=cut[jl2^1]=0;
    					if(tmp<Max(f[b[jl2].to][0],f[b[jl2].to][1])){
    						tmp=Max(f[b[jl2].to][0],f[b[jl2].to][1]);
    						judrt[b[jl2].to]=1;
    						cut[jl2]=cut[jl2^1]=1;
    					} else {
    						for(rg int j=1;j<=tp;j++){
    							f[sta[j]][0]=g[sta[j]][0],f[sta[j]][1]=g[sta[j]][1];
    						}
    						judrt[b[jl1].to]=1;
    						cut[jl1]=cut[jl1^1]=1;
    					}
    					ans+=tmp;
    				} else {
    					dfs2(i,0);
    					judrt[i]=1;
    					ans+=Max(f[i][0],f[i][1]);
    				}
    			}
    		}
    		long long ans1=ans/1000000,ans2=ans%1000000;
    		printf("%lld %lld
    ",ans1,ans2);
    		for(rg int i=1;i<=n;i++){
    			if(judrt[i]){
    				pri(i,0,f[i][1]>f[i][0]);
    				judrt[i]=0;
    			}
    		}
    	}
    	return 0;
    }
    

    B. 大水题

    分析

    因为奶牛的种类数不会很多,所以可以枚举选择的奶牛种类的状态

    强制这个区间内只能选这些种类的奶牛

    然后把差分后的状态哈希一下存进哈希表里

    比如说当前奶牛的个数分别为 (1 2 2)

    那么我们就可以把这个状态看成 (0 1 1)

    去哈希表里找一下之前有没有这样的状态就行了

    如果当前出现了不合法的奶牛种类,直接把哈希表清空即可

    代码

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #include<cstring>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=1e6+5,maxk=15,mod=1e5+3;
    typedef unsigned long long ull;
    const ull bas=2333333;
    int n,k,ans=-1,sum[maxn][maxk],cnt[maxk],totcnt,mmax,sta2[maxn],tp2;
    struct jie{
    	int wz,num;
    }c[maxn];
    bool cmp(rg jie aa,rg jie bb){
    	return aa.wz<bb.wz;
    }
    struct has{
    	int h[maxn],tot,sta[maxn],tp;
    	struct asd{
    		int nxt,beg;
    		ull val;
    	}b[maxn];
    	void init(){
    		memset(h,-1,sizeof(h));
    		tot=1;
    	}
    	void clear(){
    		for(rg int i=1;i<=tp;i++) h[sta[i]]=-1;
    		tot=1,tp=0;
    	}
    	void ad(rg ull val,rg int num){
    		rg int now=val%mod;
    		for(rg int i=h[now];i!=-1;i=b[i].nxt){
    			if(b[i].val==val) return;
    		}
    		b[tot].nxt=h[now],b[tot].val=val,b[tot].beg=num,h[now]=tot++;
    		sta[++tp]=now;
    	}
    	int cx(rg ull val){
    		rg int now=val%mod;
    		for(rg int i=h[now];i!=-1;i=b[i].nxt){
    			if(b[i].val==val) return b[i].beg;
    		}
    		return -1;
    	}
    }mp;
    int main(){
    	n=read(),k=read();
    	for(rg int i=1;i<=n;i++){
    		c[i].wz=read(),c[i].num=read();
    		totcnt=std::max(totcnt,c[i].num);
    	}
    	std::sort(c+1,c+n+1,cmp);
    	for(rg int i=1;i<=n;i++){
    		for(rg int j=1;j<=totcnt;j++) sum[i][j]=sum[i-1][j];
    		sum[i][c[i].num]++;
    	}
    	mmax=(1<<totcnt)-1;
    	rg int ncnt=0;
    	for(rg int i=1;i<=mmax;i++){
    		ncnt=0;
    		for(rg int j=1;j<=totcnt;j++){
    			if(i&(1<<(j-1))) ncnt++;
    		}
    		if(ncnt>=k) sta2[++tp2]=i;
    	}
    	rg int zt=0,now=0,lat;
    	rg ull nhas=0;
    	mp.init();
    	for(rg int i=1;i<=tp2;i++){
    		zt=sta2[i];
    		mp.clear();
    		lat=0;
    		for(rg int j=1;j<=totcnt;j++) cnt[j]=0;
    		for(rg int j=1;j<=n;j++){
    			if(zt&(1<<(c[j].num-1))){
    				cnt[c[j].num]++;
    				rg int mmax=0x3f3f3f3f;
    				for(rg int k=1;k<=totcnt;k++){
    					if(zt&(1<<(k-1))) mmax=std::min(mmax,cnt[k]);
    				}
    				for(rg int k=1;k<=totcnt;k++){
    					if(zt&(1<<(k-1))) cnt[k]-=mmax;
    				}
    				nhas=0;
    				for(rg int k=1;k<=totcnt;k++){
    					nhas=nhas*bas+(ull)k*cnt[k];
    				}
    				if(nhas==0){
    					ans=std::max(ans,c[j].wz-c[lat+1].wz);
    				}
    				now=mp.cx(nhas);
    				if(now!=-1){
    					ans=std::max(ans,c[j].wz-c[now+1].wz);
    				} else {
    					mp.ad(nhas,j);
    				}
    			} else {
    				mp.clear();
    				for(rg int k=1;k<=totcnt;k++) cnt[k]=0;
    				lat=j,nhas=0;
    			}
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    C. 佛罗里达

    分析

    把人抽象成节点,那么人和人之间的边权就是它们的矛盾值

    这种两个限制的问题肯定要把限制变成一个

    所以我们把边权从大到小排序

    枚举每一条边,强制左边的集合必须选这条边并且这条边的价值是最大的

    对于所有边权比这条边小的边,我们最后肯定可以都把它们放到左边的集合中

    所以需要考虑的只是边权比这条边大的边如何放

    我们把所有边权比这条边大的边拉出来进行奇偶染色

    如果出现了奇环肯定无解,而且之后也一定无解,直接 (break) 就行了

    否则我们肯定要把这些点分成两部分,一部分给左边的集合,一部分给右边的集合

    如果两个点在同一个联通块中并且染色相同,那么就必须划分到一个集合中

    对于和当前边的左右端点在一个联通块中并且染色相同的点,强制放到左边的集合中

    而且要满足右边的集合价值最小

    这个问题我也没有什么好的做法,直接跑一个 (dfs) 就行了

    复杂度肯定是不对的,但是跑得比正解还快,貌似也卡不住

    代码

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=255;
    int n,a[maxn][maxn],ans=0x7f7f7f7f,tot,nans;
    struct asd{
    	int zb,yb,val;
    	asd(){}
    	asd(rg int aa,rg int bb,rg int cc){
    		zb=aa,yb=bb,val=cc;
    	}
    }b[maxn*maxn];
    bool cmp(rg asd aa,rg asd bb){
    	return aa.val>bb.val;
    }
    std::vector<int> g[maxn],tmp1[maxn],tmp2[maxn];
    int cnt,shuyu[maxn],col[maxn],sta[maxn],tp,nid;
    bool vis[maxn],haha,jud[maxn];
    void dfs(rg int now,rg int ncol){
    	col[now]=ncol;
    	shuyu[now]=cnt;
    	for(rg int i=0;i<g[now].size();i++){
    		if(!col[g[now][i]]){
    			dfs(g[now][i],3-ncol);
    		} else {
    			if(col[now]==col[g[now][i]]) haha=1;
    		}
    	}
    }
    void dfs2(rg int now,rg int mans){
    	if(mans>=nans) return;
    	if(mans+b[nid].val>=ans) return;
    	if(now>cnt){
    		nans=std::min(nans,mans);
    		return;
    	}
    	rg int tmp=mans;
    	for(rg int i=0;i<tmp1[now].size();i++){
    		sta[++tp]=tmp1[now][i];
    		for(rg int j=1;j<tp;j++){
    			tmp=std::max(tmp,std::max(a[sta[tp]][sta[j]],a[sta[j]][sta[tp]]));
    		}
    	}
    	dfs2(now+1,tmp);
    	for(rg int i=0;i<tmp1[now].size();i++) tp--;
    	if(!jud[now]){
    		tmp=mans;
    		for(rg int i=0;i<tmp2[now].size();i++){
    			sta[++tp]=tmp2[now][i];
    			for(rg int j=1;j<tp;j++){
    				tmp=std::max(tmp,std::max(a[sta[tp]][sta[j]],a[sta[j]][sta[tp]]));
    			}
    		}
    		dfs2(now+1,tmp);
    		for(rg int i=0;i<tmp2[now].size();i++) tp--;
    	}
    }
    int main(){
    	while(scanf("%d",&n)!=EOF){
    		ans=0x7f7f7f7f,haha=0,tot=0;
    		for(rg int i=1;i<=n;i++){
    			for(rg int j=i+1;j<=n;j++){
    				a[i][j]=read();
    				b[++tot]=asd(i,j,a[i][j]);
    			}
    		}
    		for(rg int i=1;i<=n;i++) vis[i]=0,g[i].clear();
    		if(n==2){
    			printf("0
    ");
    			continue;
    		}
    		std::sort(b+1,b+tot+1,cmp);
    		rg int aa,bb;
    		for(rg int i=1;i<=tot;i++){
    			for(rg int j=1;j<=n;j++) jud[j]=0,shuyu[j]=0,col[j]=0,tmp1[j].clear(),tmp2[j].clear();
    			cnt=0,nid=i;
    			if(b[i].val<b[i-1].val){
    				for(rg int j=i-1;j>=1;j--){
    					if(b[j].val!=b[i-1].val) break;
    					aa=b[j].zb,bb=b[j].yb;
    					g[aa].push_back(bb),g[bb].push_back(aa);
    					vis[aa]=vis[bb]=1;
    				}
    			}
    			for(rg int j=1;j<=n;j++){
    				if(vis[j] && !shuyu[j]){
    					cnt++;
    					dfs(j,2);
    				}
    			}
    			if(haha) break;
    			if(shuyu[b[i].zb]==shuyu[b[i].yb] && col[b[i].zb]!=col[b[i].yb]) continue;
    			for(rg int j=1;j<=n;j++){
    				if(vis[j]){
    					if(shuyu[b[i].zb]==shuyu[j]){
    						if(col[b[i].zb]!=col[j]){
    							tmp1[shuyu[j]].push_back(j);
    							jud[shuyu[j]]=1;
    						}
    					} else if(shuyu[b[i].yb]==shuyu[j]){
    						if(col[b[i].yb]!=col[j]){
    							tmp1[shuyu[j]].push_back(j);
    							jud[shuyu[j]]=1;
    						}
    					} else {
    						if(col[j]&1) tmp1[shuyu[j]].push_back(j);
    						else tmp2[shuyu[j]].push_back(j);
    					}
    				}
    			}
    			nans=0x3f3f3f3f,tp=0;
    			dfs2(1,0);
    			ans=std::min(ans,nans+b[i].val);
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    适配器模式
    第五章项目:QuickHit
    试题分析(第二套)
    试题分析(第一套)
    新闻发布系统(分页显示)
    jsp九大内置对象
    文件上传
    jsp统测
    新闻发布系统(首页)
    URL和URI的区别
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14328582.html
Copyright © 2011-2022 走看看