zoukankan      html  css  js  c++  java
  • 【全程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
  • 相关阅读:
    Spring Boot2(九):整合Jpa的基本使用
    Spring Boot2(八):性感banner,在线发牌
    Spring Boot2(七):拦截器和过滤器
    我为什么要写作
    代码整洁之道
    redis-缓存穿透和缓存击穿
    云计算的三种服务模式
    java-泛型
    java-注解
    java-反射
  • 原文地址:https://www.cnblogs.com/wweiyi2004/p/15577475.html
Copyright © 2011-2022 走看看