【全程NOIP计划】思维与构造题

【全程NOIP计划】思维与构造题

什么是构造题?

不同于维护数据结构并回答询问的数据结构,寻找最大值和最小值的最值问题,计数问题,但构造体致力于让你给出一组方案,使得在一定的限制内满足某些条件,方案通常不唯一,构造方法也可以不唯一

比如:

1.给定一个排列,允许元素交换操作,输出一组操作使之排序

2.输入一个数,输出一个边长为整数的非直角三角形,且该三角形的面积为n

3.没有输入

解题方法

比如二分,分治,排序,图论,网络流,2-sat,最短路等

再比如数学公式直接求出来

还比如归纳法,先考虑通过构造小的情况,再通过小的情况构造大的情况

考虑特殊情况,比如要求构造一个特定的图,可以自己添加条件限制范围,比如特定的二分图,特定的树,特定的链等,一个常见的条件为对称性,构造具有数学美的答案

CF1438D

思路

如果只有三个数的话,一次就能操作完了

如果n=4的话,一次操作变成了aaab,或者abbb,如果a不等于b,那么就是无解

n等于5的话

a,b,c,d,e

于是我们发现了一个好办法,如果三个数是aab的形式,可以变成bbb

如果我们把数弄成aabbccdd...fff的形式,那么就可以连续使用xxf的形式将所有的数字全都变成f

操作次数顶多为(n-3)/2*2+1=n-2

如果是偶数个数呢

刚才的操作的话a,b,c,d,e,f

见ppt,如果y=f,实际上可以证明无解

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int maxn=100005;
int n,ans;
int a[maxn];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	if(!(n&1))
	{
		int qyj=0;
		for(int i=1;i<=n;i++)
		qyj^=a[i];
		n--;
		if(qyj)
		{
			cout<<"NO"<<endl;
			return 0;
		}
	}
	cout<<"YES"<<endl;
	cout<<n-2<<endl;
	for(int i=1;i+2<=n;i+=2)
	cout<<i<<" "<<i+1<<" "<<i+2<<endl;
	for(int i=1;i+1<=n-3;i+=2)
	cout<<i<<" "<<i+1<<" "<<n<<endl;
	return 0;
}

CF468C

思路

考虑一个数字x,如果\(f(x)=y\),则有\(f(x+10^{18})=y+1\),这就非常想染

我们可以设\(\sum_{i=0}^{10^{18}-1}f(i) \equiv p(mod\space a)\),则有

\(\sum_{i=0}^{10^{18}-1}\)

\(=f(10^{18}+0)+\sum_{i=1}^{10^{18}-1}f(i)\)

\(=1+\sum_{i=0}^{10^{18}-1}f(i) \equiv p+1(mod \space a)\)

similarly

我们可以推出

\(\sum_{i=2}^{10^{18}+1}f(i) \equiv p+2(mod \space a)\)

\(\sum_{i=a-p}^{10^{18}+a-p-1}f(i) \equiv 0(mod \space a)\)

所以就有

\(l=a-p,r=10^{18}+a-p-1\)时,构造出如题意的一组hack数据

于是我们需要求个p

\(\sum_{i=0}^{10^{18}-1}f(i)\)

=\(45 \times10^{17}+10 \times \sum_{i=0}^{10^{17}-1}f(i)\)

\(=45 \times 10^{17}+10 \times (45 \times 10^{16})+100 \times \sum_{i=0}^{10^{16}-1}f(i)\)

继续按照上述方式化简

\(=81 \times 10^{18}\)

\(p=81 \times 10^{18}mod \space a\)

然后就解出来了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const long long INF=1e18;
long long l,r,mod;
int main()
{
	cin>>mod;
	l=mod-INF%mod*9%mod*9%mod;
	r=l+INF-1;
	cout<<l<<" "<<r<<endl;
	return 0;
}

AT5759

题目

给定一个树,要求给出一个排列,使树上的每一对点(i,j),如果这两个点的距离为3,那么\(p_i\times p_j\)\(p_i+p_j\)中至少一个为3的倍数,树上每条边的长度为1

思路

图上的构造题目怎么做?

考虑图的特殊结构:链,菊花树,二分图,环等

从中发现某些特殊性质

我们用0,1,2表示模3余0,1,2的数

如果是一条链的话,直接构造1212121212.......0000.....

怎么拓展到一般的树呢?

考虑另外的一种情况,菊花图如何构造呢?

第一层 1,第二层为2,其余地方用0来填充

自然而然,我们可以想到对树进行黑白染色

两个相距为3的点颜色一定不同

对于黑白集合中的较小的集合,如果它大于n/3,那么填入所有的1

P5541

思路

即要求这四个城市必须强连通

正难则反

我们反过来考虑四个点什么时候不是强连通,以减少此情况的数量

要么一个点没有入边,要膜一个点没有出边

要么两组点之间只有一个方向的边

尝试减少一个点没有入边的情况。总共的出边的个数为n(n-1)/2-n=n(n-3)/2

如果设i的出边个数为S(i),那么总的情况数量为C(S(i),3)

可以证明S(i)=(n-3)/2的时候最小

进行如下构造,将n个点放在一个圆内接正n边形的顶点上,最长的对角线为无向边,每个点都向顺时针接下来的(n-3)/2个点连一条有向边

我们发现满足S=(n-3)/2

其他情况并不存在

第二种情况已经包含在第一种情况

第三种情况也可以验证不存在

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
int n;
int a[105][105];
int main()
{
	cin>>n;
	if(n==1)
	{
		cout<<0<<endl<<0<<endl;
		return 0;
	}
	cout<<n*(n-3)*(n*n+6*n-31)/48<<endl;
	int m=(n+1)>>1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=(n+1)/2;j++)
		a[i][(i+j-1)%n+1]=1;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		cout<<a[i][j]<<" ";
		cout<<endl;
	}
	return 0;
}

覆盖完全图

假设一个有n个点的无向完全图,n为奇数,那么它有n(n-1)/2条边

现在需要构造(n-1)/2个长为n的简单环覆盖这个图,且这些环的边不可重合

比如n在等于3的时候,显然,直接构造1-2-3

n=5的时候可以有1-2-3-4-5,1-3-5-2-4

第一个尝试

构造一堆等差数列

这些边永远都不会重合

思考

如何设立一个特殊条件?

把1从每个环里面拿出来,那么就变成了(n-1)/2条边不重合的链,且这些链的首尾元素互不相同

问题转化为如何在大小为偶数的图中找这种链

对称地构造方案

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=(n-1)/2;i++)
    {
        int a=i+1,b=i+1
    }
}

CF1103C

题目

给出一个无重边的无向图,每个点的度数大于等于3,和一个限制k,需要你构造以下两种情况中的一种:

1.找出一条路径长度为n/k

2.找出k个环,使得每个环的长度大于3并且不是3的倍数,并且要求保证每个环中至少有一个点在这k个环里只出现一次

思路

构造图的另一个套路:无向图太麻烦了,考虑图的生成树

在数学竞赛的证明上也有用处QAQ

考虑一个dfs生成树

如果生成树的深度n>=n/k,问题不就解决了吗

否则存在k个不同的叶子

图中的dfs生成树

满足条件:所有非树边连接的都是祖先

由于度数大于等于3,对于叶子k存在两个祖先x,y使得k连向x,y

这样有三个环

如果前两个是三的倍数,那么第三个一定不是3的倍数,因为dep(y)-dep(x)是3的倍数

P7115

即NOIP2020的一道题目(PS:giao,就是因为我csp爆零了才去不了的,kk

思路

考虑两种颜色的情况。也就是说有2个柱和一个额外的空柱

对于一个柱A,怎么把0和1分开呢?

暂时利用另一个柱B,首先,计算0的个数为x,将B上x个移到额外的柱子C上

接着不断移动A上面的球,如果是0就移动到B,否则移动到C

最后吧0,1的段依次移回去,B上的x个也移回去

用了多少次?

x+m+m+x<=m+m+m+m=4m

如果把x改成0,1个数中较小的那个,那么我们有x+m+m+x<=3m

如果拓展到n种颜色?

用分治,把1~n/2视为颜色0,n/2到n视为颜色1

严格定义递归的过程

solve(l,r)表示所有l-r的颜色的球都在柱子l-r,每次需要维持额外柱是空,

对每个柱子排序需要3nm

如何那个颜色1的球都放到后一半?

对于两个柱子,如果他们中1的数量超过m,那么可以将所有0放到空柱子,然后制造出一个1的柱子,然后再把0放进去,如果它们中0的数量大于m,那么可以首先将一个柱全都放到空柱,然后另一个柱把0放过来,然后再依次从空柱放0和1过来,制造出一个0的在柱子

CF1148F

思路

一个想法:既然只是要求符号变反而已,那么直接测试一些特定的s如何?

显然可以被叉掉,所以它FST了

考虑mask小于2的情况。要么是全部去翻,要么是全部不取反

假设我们已经解决了mask小于2的k次方的

本博文为wweiyi原创,若想转载请联系作者,qq:2844938982
原文地址:https://www.cnblogs.com/wweiyi2004/p/15577475.html