1. 程式人生 > 其它 >【全程NOIP計劃】思維與構造題

【全程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