zoukankan      html  css  js  c++  java
  • [构造题选讲]

    构造,人类智慧题。

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

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

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

    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;;
    }
    
  • 相关阅读:
    如何将自己的镜像上传到私库
    基于spring-cloud的微服务(1) 服务注册中心eureka
    关于对象池技术的一些记录
    为Docker容器中运行的gitlab添加ssh的一些问题记录
    使用java实现的socket代理(支持socket4和socket5)
    ConfluenceRemoteUserAuth
    JiraRemoteUserAuth
    Apache 的mod_auth_cas模块的介绍和使用
    基于乌班图的标准镜像添加中文支持
    apache反向代解决绝对路径可能出现的问题
  • 原文地址:https://www.cnblogs.com/dixiao/p/15050370.html
Copyright © 2011-2022 走看看