【全程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次方的