首页 > 其他分享 >构造题学习笔记

构造题学习笔记

时间:2023-02-20 21:34:13浏览次数:53  
标签:10 int ++ 构造 学习 vis 笔记 include 节点

抽屉原理

在构造题中,若我们遇到了 \(n/k\) 这样的操作次数的时候,可以考虑将所有数划分为 \(k\) 个集合。这样,最小的那个集合的大小就一定小于等于 \(n/k\) 了。

CF1198C

给定一张有 \(3n\) 个点,\(m\) 条边的无向图。请找到一个大小为 \(n\) 的点独立集或边独立集。

\(n \leq 10^5,m \leq 5 \times 10^5\)

思路

维护一个点集和边集。初始时点集为全集,边集为空。

遍历 \(m\) 条边,若其两端点都在点集中,则将两点删去,并将边加入边集。

点集的大小设为 \(x\),边集的大小设为 \(y\),那么边集中的点数为 \(2y\),且与点集中的点无交集。满足 \(x+2y=3n\),那么根据抽屉原理,\(\min{x,y} \geq n\)。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e5+10;
int n,m,x,y,u[N],v[N],id[N];bool del[N];
void solve()
{
	scanf("%d%d",&n,&m);x=3*n,y=0;for(int i=1;i<=3*n;i++) del[i]=false;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u[i],&v[i]);if(del[u[i]]||del[v[i]]) continue;
		del[u[i]]=del[v[i]]=true;x-=2,id[++y]=i;
	}
	if(x>=n)
	{
		puts("IndSet");int cnt=0;
		for(int i=1;i<=3*n&&cnt<n;i++) if(!del[i]) printf("%d ",i),cnt++;
		puts("");
	}
	else
	{
		puts("Matching");
		for(int i=1;i<=n;i++) printf("%d ",id[i]);
		puts("");
	}
}
int main()
{
	int T;scanf("%d",&T);while(T--) solve();
	
	return 0;
}

CF1534D

这是一道交互题。

目标:你需要求出一棵树的形态。

询问:一个数 \(x\),然后将得到 \(n\) 个数表示各个点到 \(x\) 的距离。限 \(⌈n/2⌉\) 次。

思路

不难发现,一个点和与其距离为 \(1\) 的点相邻。考虑询问出所有相邻的点得到树的形态。

首先询问根节点,将其余所有的节点分为两类,一类到根节点距离为偶数,另一类到根节点距离为奇数。根据抽屉原理,这两类节点的最小值不超过 \(⌈n/2⌉\)。那么只需对较小的那类节点进行询问,将距离为 \(1\) 的点连边即可。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=2023;
int n,x[N],y[N],u[N],v[N],d[N],tot,tx,ty;
int main()
{
	cin>>n;cout<<"? 1"<<endl;fflush (stdout);
	for(int a,i=1;i<=n;i++)
	{
		cin>>a;if(i==1) continue;d[i]=a;
		if(a%2) x[++tx]=i;else y[++ty]=i;
	}
	if(tx>ty)
	{
		for(int i=2;i<=n;i++) if(d[i]==1) u[++tot]=1,v[tot]=i;
		swap(x,y),swap(tx,ty);
	}
	for(int i=1;i<=tx;i++)
	{
		cout<<"? "<<x[i]<<endl;
		fflush(stdout);
		for(int j=1;j<=n;j++)
		{
			int a;cin>>a;
			if(a==1) u[++tot]=x[i],v[tot]=j;
		}
	}
	cout<<"!"<<endl;
	for(int i=1;i<n;i++) cout<<u[i]<<" "<<v[i]<<endl;
	return 0;
} 

CF1450C

给定一张 \(n\) 行 \(n\) 列的棋盘,每个格子可能是空的或包含一个标志,标志有 \(\text{X}\) 和 \(\text{O}\) 两种。

如果有三个相同的标志排列在一行或一列上的三个连续的位置,则称这个棋盘是一个 胜局, 否则称其为 平局

在一次操作中,你可以将一个 \(\text X\) 改成 \(\text O\),或将一个 \(\text O\) 改成 \(\text X\)。

设棋盘中标志的总数为 \(k\),你需要用不超过 \(\lfloor \frac{k}{3}\rfloor\) 次操作把给定的局面变成平局。

\(n \leq 300\)。

思路

注意到能够构成胜局的三个位置,它们的横纵坐标和模 \(3\) 的余数各不相同。考虑将所有格子依照模 \(3\) 的余数分为 \(3\) 类。

此时只需要保证其中两类格子互不相同,剩下一类格子不变,也就不会出现胜局了。根据这种思路,一共有 \(6\) 种修改方案。每一个包含标志的格子,更改其颜色的方案有两种,那么总的更改次数就是 \(2k\)。根据抽屉原理,其中最小的修改次数不超过 \(\lfloor \frac{k}{3}\rfloor\)。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=310;
char a[N][N],s[N][N];int n,m;
bool check(char t[])
{
	int cnt=0;
	for(int i=1;i<=n;i++) 
	    for(int j=1;j<=n;j++)
	    {
	    	if(t[(i+j)%3]=='O'&&s[i][j]=='X') cnt++,a[i][j]='O';
	    	else if(t[(i+j)%3]=='X'&&s[i][j]=='O') cnt++,a[i][j]='X';
	    	else a[i][j]=s[i][j];
		}
	return cnt<=m/3;
}
void print(){for(int i=1;i<=n;i++,puts("")) for(int j=1;j<=n;j++) putchar(a[i][j]);}
void solve()
{
	scanf("%d",&n);m=0;for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) m+=(s[i][j]!='.');
	if(check(".XO")) return print(),void();
	if(check(".OX")) return print(),void();
	if(check("X.O")) return print(),void();
	if(check("O.X")) return print(),void();
	if(check("XO.")) return print(),void();
	if(check("OX.")) return print(),void();
}
int main()
{
	int T;scanf("%d",&T);while(T--) solve();
	return 0;
}

归纳法

考虑找到有解的充要条件,然后试图将原问题转为规模减一的一定有解的子问题。

CF1470D

给你一张图,你需要给一些点染色,要求:

1.一条边两端不能都染色了。
2.仅保留有一端染色了的边,图仍然连通。

判断是否有解,若是给出构造

思路

假设当前已经将一个大小为 \(k\) 的点集合法染色。考虑将点 \(i\) 加入到点集中。

如果点集中存在一个点 \(j\),满足点 \(j\) 与点 \(i\) 有边直接相连,且点 \(j\) 被染色了,那么直接将点 \(i\) 加入点中,仍然合法;

若点集中不存在这样的点 \(j\),那么将点 \(i\) 染色后加入点集中,仍然合法。

按照这样的步骤逐步扩大点集,如果原图联通,那么必定有解。

同时为了保证新枚举的点与点集中的点有边直接相连,可以按照 dfs 枚举图上的所有点。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3e5+10,M=6e5+10;
int h[N],tot,cnt,n,m,idx,q[N];bool vis[N],col[N];
struct edge{int v,nex;}e[M];
void add(int u,int v){e[++idx]=edge{v,h[u]};h[u]=idx;}
void dfs(int u)
{
	vis[u]=true,cnt++;bool used=0;
	for(int v,i=h[u];i;i=e[i].nex) if(vis[v=e[i].v]) used|=col[v];col[u]=used^1;tot+=col[u];
	for(int v,i=h[u];i;i=e[i].nex) if(!vis[v=e[i].v]) dfs(v);
}
void solve()
{
	scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) h[i]=col[i]=vis[i]=0;idx=tot=cnt=0;
	for(int u,v,i=1;i<=m;i++) scanf("%d%d",&u,&v),add(u,v),add(v,u);dfs(1);
	if(cnt<n) return puts("NO"),void();puts("YES");printf("%d\n",tot);
	for(int i=1;i<=n;i++) if(col[i]) printf("%d ",i);puts("");
}
int main()
{
	int T;scanf("%d",&T);while(T--) solve();
	return 0;
}

CF1515F

给定一张图,点带权,初始为 \(

标签:10,int,++,构造,学习,vis,笔记,include,节点
From: https://www.cnblogs.com/ListenSnow/p/17138574.html

相关文章

  • 华为eNSP学习笔记
    严正声明:全篇内容为原创内容,版权归属博客园用户Hmi1234所有,仅供学习和参考,未经允许严禁转载!网络组建与应用第1章华为VRP系统基本操作1.0.1用户视图(查看命令)......
  • 【博学谷学习记录】超强总结,用心分享 | this/call/apply/bind
    this的指向问题在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。简单例子......
  • 2023年2月20日软件工程学习总结
    今天上课更加清楚的认识到了自学的方法的重要性,最后一节课留下来做测试,由于之前的主客观原因只是在网络上查找了一些模板去完成向数据库添加数据的操作,但没能实现数据库的......
  • Vue学习随笔(一)Vue的引入
    前言以往零零散散使用过一些Vue的语法,最近才刚刚系统接触Vue,现在是刚刚入门的状态,故在这里做一个记录和梳理,欢迎大家一起学习交流,有错误的地方也欢迎大家指正。正篇梦开......
  • 机器学习技术系列:【机器学习工程化平台 Kubeflow】简介
    导言如今,很多科技企业都投入了对机器学习技术的研究和应用中。但是面临的情况可能是组织已经在本地使用机器学习,但还不能够将其部署到生产环境中;或者能够部署模型,但无法对......
  • C语言学习中比较奇怪的问题(1)int a = 1 ; int sum = (++a) + (++a) + (++a) ;
    题目:inta=1;  intsum=(++a)+(++a)+(++a); 当前想法:sum=2+3+4= 9 结果:   sum=10 原因:key——寄存器第①个++a......
  • 寒假学习记录(三)
    这个作业属于哪个课程<班级的链接>这个作业要求在哪里<作业要求的链接>这个作业的目标学有所得一、写在前面对于每次寒假作业的目标,我给自己设定的都......
  • Java学习笔记----关于集合框架
    集合框架的概述集合、数组都是多个数据进行存储操作的结构,简称java容器说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储数组在存储多个数据方面的特点一......
  • Intel汇编语言程序设计笔记
    ⦁2^8=2562^10=10242^16=65536[二进制]1111=F[16进制]⦁ 有符号二进制整数的最高有效位[MSB]表示数的符号,0=正数1=负数⦁ 数据的意义,由其数据类型决定,单纯的数字没......
  • Python 学习02 内置数据类型
    ......