zoukankan      html  css  js  c++  java
  • Codeforces Round #720 (Div. 2)

    C.Nastia and a Hidden Permutation

    题目描述

    点此看题

    有一个长度为 (n) 的未知排列,可以询问 ((t,i,j,x)),会返回如下值:

    • (t=1:max(min(x,p_i),min(x+1,p_j)))
    • (t=2:min(max(x,p_i),max(x+1,p_j)))

    最多询问 (lfloorfrac{3cdot n}{2} floor+30) 次后确定这个排列

    (3leq nleq 10^4)

    解法

    有一种思路是询问直接拿答案,如果用第一种操作拿答案,(p_i=1,x=n-1) 返回的就是 (p_j)

    那么问题变成了找到 (p_i=1)(i),要求在 (frac{n}{2}) 次之内找到。可以用第二种操作找。令 (x=1) 那么如果返回值是 (1) 就知道 (p_i=1),可以一对一对的找,如果返回值是 (2) 那么 (p_jleq 2),对 (p_j) 再问一次即可,否则 (p_j>2) 就不用问他了。

    #include <cstdio>
    const int M = 10005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,p,a[M];
    int ask(int a,int b,int c,int d)
    {
    	printf("? %d %d %d %d
    ",a,b,c,d);
    	fflush(stdout);
    	return read();
    }
    signed main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();p=0;
    		for(int i=1;i<=n/2;i++)
    		{
    			int t1=ask(2,i,i+n/2,1);
    			if(t1==1) {p=i;break;}
    			else if(t1==2)
    			{
    				int t2=ask(2,i+n/2,i,1);
    				if(t2==1) {p=i+n/2;break;}
    			}
    		}
    		if(n%2 && !p) p=n;a[p]=1;
    		for(int i=1;i<=n;i++)
    		{
    			if(i!=p) a[i]=ask(1,p,i,n-1);
    		}
    		printf("!");
    		for(int i=1;i<=n;i++)
    			printf(" %d",a[i]);
    		puts("");
    		fflush(stdout);
    	}
    }
    

    D. Nastia Plays with a Tree

    题目描述

    点此看题

    给你一棵 (n) 个点的树,每次可以删掉一条树边任意加一条边(不要求联通),求最少操作数,要求输出方案。

    (2leq nleq 10^5)

    解法

    这道题有一个迷惑点就是不要求联通,但是最优的方案是可以保证时时刻刻联通的。

    其实可以树形 (dp)不考虑父边,子树内一定要构成一条链才行,但是考虑父边就有两种情况。定义纯链表示以子树根 (u) 为端点的链,定义中转链表示 (u) 为中转点的链,设 (dp[u][0/1]) 分别表示纯链(/)中转链的最小操作次数,转移:

    • 纯链可以找到子树内的一条纯链继承,剩下的都选中转链接到这个纯链上,找 (dp[v][1]+1-dp[v][0]) 最大的即可。
    • 中转链可以找到子树内的两条纯链继承,剩下的都选中转链接到这个纯链上,找 (dp[v][1]+1-dp[v][0]) 最大和次大的即可。

    剩下的问题就是输出方案了,转移的时候记录一下父亲某个状态是否使用了儿子的纯链状态来转移,然后再搞一个 ( t dfs),设 (l[u]) 为链的左端点,(r[u]) 为链的右端点,讨论一下即可,时间复杂度 (O(n))

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 100005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,tot,f[M],dp[M][2],pd[M][2],l[M],r[M];
    struct edge
    {
    	int v,next;
    }e[2*M];
    void dfs(int u,int fa)
    {
    	int mx1=0,mx2=0,p1=0,p2=0;
    	dp[u][0]=dp[u][1]=pd[u][0]=pd[u][1]=0;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==fa) continue;
    		dfs(v,u);
    		dp[u][0]+=dp[v][1]+1;
    		dp[u][1]+=dp[v][1]+1;
    		int t=dp[v][1]+1-dp[v][0];
    		if(mx1<=t)
    		{
    			mx2=mx1;p2=p1;
    			mx1=t;p1=v;
    		}
    		else if(mx2<=t) mx2=t,p2=v;
    	}
    	dp[u][0]-=mx1;
    	dp[u][1]-=mx1+mx2;
    	pd[p1][0]=pd[p1][1]=pd[p2][1]=1;
    }
    void print(int u,int tp,int fa)
    {
    	l[u]=r[u]=u;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==fa) continue;
    		if(pd[v][tp])
    		{
    			print(v,0,u);
    			if(l[u]==u) l[u]=l[v];
    			else r[u]=l[v];//r[v]=v,所以右端点是l[v] 
    		}
    	}
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==fa) continue;
    		if(!pd[v][tp])
    		{
    			print(v,1,u);
    			printf("%d %d %d %d
    ",u,v,l[u],l[v]);
    			l[u]=r[v];
    		}
    	}
    }
    signed main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();tot=0;
    		for(int i=1;i<=n;i++) f[i]=0;
    		for(int i=1;i<n;i++)
    		{
    			int u=read(),v=read();
    			e[++tot]=edge{v,f[u]},f[u]=tot;
    			e[++tot]=edge{u,f[v]},f[v]=tot;
    		}
    		dfs(1,0);
    		printf("%d
    ",min(dp[1][0],dp[1][1]));
    		if(dp[1][0]<=dp[1][1]) print(1,0,0);
    		else print(1,1,0);
    	}
    }
    

    E. Nastia and a Beautiful Matrix

    题目描述

    点此看题

    (k) 种颜色,第 (i) 种颜色数量是 (a_i),数量总和是 (m),求满足下列染色的最小正方形,:

    • 对于每个 (2 imes2) 子矩形颜色数量不超过 (3)
    • 对于每个 (2 imes 2) 子矩形的对角线颜色不同,未染色的不算在其中

    (1leq m,kleq 10^5)

    解法

    ( t oneindark) 告诉我们:这道题限制比较复杂,不好 ( t dp) 之类的,应该是构造了。然后这种相邻格子不同的限制可以往对矩阵染色方面想

    考虑 (n imes n) 的矩形是否满足条件,把原图划分成若干个 (2 imes 2) 的子矩形可以得到答案上界:(mleq n^2-lfloorfrac{n}{2} floor^2,max a_ileq ncdot lceilfrac{n}{2} ceil),那么考虑构造出这个答案上界,我们把矩形按下列方法染色,用一下官方的图:

    我们让白色格子空出来,那么蓝色格子是必须填的,相邻的黄色格子和红色格子必须不同。可以贪心构造,设 (a_p=max a_i),那么我们先解决颜色 (p),我们优先把他填入红色格,如果红色格塞满了就填蓝色格,不难发现 (a_pleq) 红蓝格总数,也就是 (max a_ileq ncdot lceilfrac{n}{2} ceil)

    其他的颜色就类似地填就行了,不难发现按这样填是一定不会冲突的,还有一个限制就是 (mleq)可填格子总数,也就是 (mleq n^2-lfloorfrac{n}{2} floor^2)

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    const int M = 100005;
    #define pii pair<int,int>
    #define make make_pair
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,m,ans[1005][1005];pii a[M];
    void work()
    {
    	n=read();m=read();
    	for(int i=1;i<=m;i++)
    		a[i].first=read(),a[i].second=i;
    	sort(a+1,a+1+m);
    	for(int s=1;;s++)
    	{
    		if(n>s*s-(s/2)*(s/2)) continue;
    		if(a[m].first>s*((s+1)/2)) continue;
    		vector<pii> x,y,z;
    		for(int i=1;i<=s;i++)
    			for(int j=1;j<=s;j++)
    				ans[i][j]=0;
    		for(int i=1;i<=s;i++)
    			for(int j=1;j<=s;j++)
    			{
    				if((i+j)%2)
    				{
    					if(i%2) x.push_back(make(i,j));
    					else y.push_back(make(i,j));
    				}
    				else if(i%2)
    					z.push_back(make(i,j));
    			}
    		for(int i=m;i>=1;i--)
    		{
    			int p=a[i].first,q=a[i].second;
    			vector<pii> &cur=(x.empty())?y:x;
    			while(!cur.empty() && p)
    			{
    				pii t=cur.back();cur.pop_back();
    				ans[t.first][t.second]=q;p--;
    			}
    			while(p--)
    			{
    				pii t=z.back();z.pop_back();
    				ans[t.first][t.second]=q;
    			}
    		}
    		printf("%d
    ",s);
    		for(int i=1;i<=s;i++,puts(""))
    			for(int j=1;j<=s;j++)
    				printf("%d ",ans[i][j]);
    		return ;
    	}
    }
    signed main()
    {
    	T=read();
    	while(T--) {work();}
    }
    
  • 相关阅读:
    查询数据库锁的SQL
    注解学习实例(模拟hibernate,table,column注解,拼装SQL)
    mongoDB学习笔记
    拼装SQL.例子
    MySQL实现类似Oracle序列的函数
    面试总结
    linux下常用命令
    PHP 中 flush() 与 ob_flush() 的区别
    PHP 使用共享内存的资料
    移动设备的web站开发和将web封转成移动端应用的一些资料
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14752891.html
Copyright © 2011-2022 走看看