很喜欢做构造题,主要是喜欢那种摸不着头脑的感觉
A. Make It Connected
题意:给出一张没有重边和自环的图,接下来定义一种操作:选择一个结点v,对于除了这个结点的其他点,如果和v之间原来有边,删除这条边;反之添加一条边。求至少操作几次能使整张图联通。
解:找特殊情况+分类讨论
首先用心感受一下这种操作,是让一个点和原来的连通块断绝连系,然后使剩下的部分联通。如果这个点和原来的连通块中的某个点之间没有边,那么一次操作后就可以满足要求。归纳一下就是,如果存在一个连通块不是完全图,那么1次操作就可以解决;现在假设全部连通块都是完全图,那进行一次操作就是把一个点从一个完全图里剥离出来。如果有超过两个的完全图,那一次操作可以使剩余的完全图不是完全图,从而两次操作后满足要求;如果只有两个完全图,那只能把小的那个删到只有一个结点,然后再操作一次使其满足要求。
B. Rectangular Congruence
题意:给出一个n*n矩阵对角线上的元素,其中n是质数。试在此基础上填完这个矩阵,使得每个子矩阵两对角之和模n不相等。即:
解:条件变形
给上式移项变形,得出结论:任意两列上位于同一行的两个点,其模n意义下的差两两不相等。让每行相邻两个数的差d相同,行与行之间d取不同值,可取0~n-1,因为n是质数,所以不会同时出现某n的倍数。
C. Equal Binary Subsequences
题意:给出一个01串,定义一种操作:选择01串的一个子序列(subsequence,可为空),将子序列循环右移一格。现执行一次这种操作,问新序列是否能分成两个相同的子序列。
解:分组构造
完全没想出来。将只有两种字符的字符串分成相同的两个子序列,考虑两两相邻分组。如果能分出(0,0),(1,1)这样的组,两个子序列各选一个0或1.如果分出(0,1)这样的组,可以通过循环右移来将其变为(0,0)或(1,1).
具体代码:
#include <bits/stdc++.h> using namespace std; #define maxx 200005 #define maxn 25 #define maxm 205 #define ll long long #define inf 1000000009 #define mod 2520 char a[maxx]={0}; signed main(){ int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); n*=2; scanf("%s",a+1); for(int i=1;i<=n;i++){ a[i]-='0'; } int cnt1=0; for(int i=1;i<=n;i++){ if(a[i]==1) cnt1++; } if(cnt1%2||n%2){ printf("-1\n"); continue; } int now=0; vector<int> ans; for(int i=1;i<=n;i+=2){ if(a[i]!=a[i+1]){ if(a[i]==now) ans.push_back(i); else ans.push_back(i+1); now=1-now; } } vector<int> ans2; now=0; for(int i=1;i<=n;i+=2){ if(a[i]==a[i+1]) ans2.push_back(i); else{ if(a[i]==now) ans2.push_back(i+1); else ans2.push_back(i); now=1-now; } } printf("%d ",ans.size()); for(auto i:ans) printf("%d ",i); printf("\n"); for(auto i:ans2) printf("%d ",i); printf("\n"); } return 0; }