[构造题选讲]

构造,人类智慧题。

使用数学公式直接求出构造方法。可能需要一些数学功底。

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

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

CF1438D Powerful Ksenia

考虑这种三位操作的,我们一般可以考虑是否有连续的操作。

那么我们发现如果当\(n\)为奇数则一定有一种可行的操作。

即将序列变为\((a1,a1,a2,a2,a3,a3,a3)\)类,再操作即可。

\(n\)为偶数,我们发现一次操作不会改变从左到右整个序列的异或值,则当整个序列异或为\(0\)时,我们只要对前\(n - 1\)个数操作即可。

// Problem: CF1438D Powerful Ksenia
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1438D
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<cstdio>
#define ll long long
#define N 100005

ll n,a[N];

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i)
	scanf("%lld",&a[i]);
	if(n % 2 == 1){
		std::cout<<"YES"<<std::endl;
		std::cout<<n - 1<<std::endl;
		for(int i = 1;i <= n - 2;i += 2)
		std::cout<<i<<" "<<i + 1<<" "<<i + 2<<std::endl;
		for(int i = n - 2;i >= 1;i -= 2)
		std::cout<<i<<" "<<i + 1<<" "<<i + 2<<std::endl;
	}else{
		ll x = 0;
		for(int i = 1;i <= n;++i)
		x = x ^ a[i];
		if(x != 0)
		std::cout<<"NO"<<std::endl;
		else{
		n -= 1;
		std::cout<<"YES"<<std::endl;
		std::cout<<n - 1<<std::endl;
		for(int i = 1;i <= n - 2;i += 2)
		std::cout<<i<<" "<<i + 1<<" "<<i + 2<<std::endl;
		for(int i = n - 2;i >= 1;i -= 2)
		std::cout<<i<<" "<<i + 1<<" "<<i + 2<<std::endl;			
		}
	}
}

AT5759 ThREE

考虑在树上黑白染色,则发现距离为3的都是不同颜色的点。

那么考虑到\(x\ mod\ 3 = 0\)的点是放哪都行的。

那么我们只要让另外两种分属不同的颜色的点的就行了。

分类讨论:\(x < \frac{n}{3}\)\(x,y > \frac{n}{3}\)

// Problem: AT5759 ThREE
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT5759
// Memory Limit: 1000 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<cstdio>
#define ll long long
#define N 400005

ll n;

struct P{
	int to,next;
}e[N << 1];

ll head[N],cnt;

inline void add(int x,int y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}

ll co1[N],co2[N],cnt1,cnt2;

ll ca[N];

inline void dfs(int u,int fa){
	ca[u] = ca[fa] ^ 1;
	if(ca[u] == 0){
		co1[++cnt1] = u;
	}else {
		co2[++cnt2] = u;
	}
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa)continue;
		dfs(v,u);
	}
}

ll ans[N];
ll c1 = 1,c2 = 2,c3 = 3;
	
inline ll find1(){return c1 > n ? 0 : c1;}
inline ll find2(){return c2 > n ? 0 : c2;}
inline ll find3(){return c3 > n ? 0 : c3;}

int main(){
	scanf("%lld",&n);
	for(int i = 1;i < n;++i){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	if(cnt1 > cnt2)std::swap(co1,co2),std::swap(cnt1,cnt2);
	if(cnt1 < n / 3){
		for(int i = 1;i <= cnt1;++i){
			ans[co1[i]] = find3();
			c3 += 3;
		}
		for(int i = 1;i <= cnt2;++i){
			if(find1()){
				ans[co2[i]] = find1();
				c1 += 3;
			}else{
				if(find2()){
					ans[co2[i]] = find2();
					c2 += 3;
				}else{
					ans[co2[i]] = find3();
					c3 += 3;
				}
			}
		}
	}else{
		for(int i = 1;i <= cnt1;++i){
			if(find1()){
				ans[co1[i]] = find1();
				c1 += 3;
			}else{
				ans[co1[i]] = find3();
				c3 += 3;
			}
		}
		for(int i = 1;i <= cnt2;++i){
			if(find2()){
				ans[co2[i]] = find2();
				c2 += 3;
			}else{
				ans[co2[i]] = find3();
				c3 += 3;
			}			
		}
	}
	for(int i = 1;i <= n;++i)
	std::cout<<ans[i]<<" ";
}

【XR-2】伤痕

考虑一组不强连通的四个点有3种可能:

1.从一个点向另外三个点各连一条有向边

2.在不满足第一种的条件下,三个点往一个点连有向边。

  1. A 与 B、C 与 D 之前都是无向边,但从 A 向 CD 各连一条有向边,从 B 向 CD 各连一条有向边。

记点\(i\)向其他点连的有向边有\(S_i\)条。因为\(n\)为奇数,有:

\(\sum_{i = 1}^{n}S_i = \frac{n * (n - 1)}{2} - n = n * \frac{n - 3}{2}\)

又因为\(C^{3}_{x}\)是凸函数。

所以\(X = \sum_i^{n}C^{3}_{s_i} > n * C^3_{\frac{n - 3}{2}}\)

所以我们只要构造一个只有这么多的第一种可能,没有第二种可能,没有第三种可能的情况。

将$ n $个点放在一个圆内接正 n 边形的顶点上,所有最长的对角线为无向边,每个点都向顺时针接下来的\frac{n - 3}{2} 个点连一条有向边。

答案为\(C^4_n - n * C^3_{\frac{n - 3}{2}}\)

CF468C Hack it!

考虑\(f(x) = y,f(x + 1e18) = y + 1\)

那么考虑\(\sum_0^{1e18 - 1} f(i) = p(mod\ a)\)

那么\(\sum_l^{1e18 + l - 1} = p + l(mod\ a)\)

那么我们只要求出\(p\)是多少就好了。

注意到\(\sum_0^{1e18 - 1} = 45 \times 1e17 + 10 \times \sum_0^{1e17 - 1}f(i)\)

所以\(\sum_0^{1e18 - 1} = 18 * 45 * 1e17\)

[NOIP2020] 移球游戏

先考虑两个颜色的时候怎么做。

然后进行分治处理。

// Problem: P7115 [NOIP2020] 移球游戏
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7115
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<cstdio>
#define ll long long 
#define N 55
#define M 405
#define K 820005

int a[N][M],top[N],n,m,ans[K][2],tot;
bool f[N];

inline void pour(int x,int y){
	ans[++tot][0] = x,ans[tot][1] = y;
	a[y][++top[y]] = a[x][top[x]--];
}

inline void del1(int x,int y,int k){//还原x
	int s = 0;
	for(int i = 1;i <= m;++i)
	s += (a[x][i] <= k);
	for(int i = 1;i <= s;++i)
	pour(y,n + 1);
	while(top[x])a[x][top[x]] <= k ? pour(x,y) : pour(x,n + 1);
	for(int i = 1;i <= s;++i)pour(y,x);
	for(int i = 1;i <= m - s;++i)pour(n + 1,x);
	for(int i = 1;i <= m - s;++i)pour(y,n + 1);
	for(int i = 1;i <= m - s;++i)pour(x,y);
	while(top[n + 1]){
		if(top[x] == m || a[n + 1][top[n + 1]] > k)
		pour(n + 1,y);
		else
		pour(n + 1,x);
	}
}

inline void del2(int x,int y,int k){
	int s = 0;
	for(int i = 1;i <= m;++i)
	s += (a[x][i] > k);
	for(int i = 1;i <= s;++i)
	pour(y,n + 1);
	while(top[x])a[x][top[x]] > k ? pour(x,y) : pour(x,n + 1);
	for(int i = 1;i <= s;++i)pour(y,x);
	for(int i = 1;i <= m - s;++i)pour(n + 1,x);
	for(int i = 1;i <= m - s;++i)pour(y,n + 1);
	for(int i = 1;i <= m - s;++i)pour(x,y);
	while(top[n + 1]){ 
		if(top[x] == m || a[n + 1][top[n + 1]] <= k)
		pour(n + 1,y);
		else
		pour(n + 1,x);
	}	
}

#define mid ((l + r) >> 1)

inline void solve(int l,int r){
	if(l == r)return;
	int li = l,ri = mid + 1;
	while(li <= mid && ri <= r){
		int s = 0;
		for(int i = 1;i <= m;++i)
		s += (a[li][i] <= mid);
		for(int i = 1;i <= m;++i)
		s += (a[ri][i] <= mid);
		if(s >= m){
			del1(li,ri,mid);
			li ++ ;
		}else{
			del2(ri,li,mid);
			ri ++ ;
		}
	}
	solve(l,mid),solve(mid + 1,r);
}

int main(){
	scanf("%lld%lld",&n,&m);
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j){
		scanf("%lld",&a[i][++top[i]]);
	}
	solve(1,n);
	std::cout<<tot<<std::endl;
	for(int i = 1;i <= tot;++i)
	std::cout<<ans[i][0]<<" "<<ans[i][1]<<std::endl;;
}
posted @ 2021-07-23 20:11  fhq_treap  阅读(142)  评论(0编辑  收藏  举报