Potato的暑期训练day#1题解 ——毒瘤构造
Potato的暑期训练day#1 ——毒瘤构造
题目链接:
A.https://vjudge.net/problem/HDU-1214
B.https://vjudge.net/problem/CodeForces-1174D
C.https://vjudge.net/problem/CodeForces-1166E
D.https://vjudge.net/problem/HihoCoder-1873
F.https://vjudge.net/problem/CodeForces-1159D
G.https://vjudge.net/problem/CodeForces-1130E
A - 圆桌会议
题意:对一个1,2,3,....n构成的圆桌进行操作,每次只能交换相邻两位,提问最少需要多少次操作可以是的这个圆桌的位置转置,即每个人左边和右边的人交换了
思路:考虑1,2,3,4,..n的线性排序经过相邻交换的操作到达n-k-1 n-k-2 ....3 2 1 n n-1 n-2........ n-k这样的排序的最少步数,转化为以上的排序何时逆序对最少,显然在中间的时候,对于每一段长度为k的逆序对总和可以使用 \(k(k-1)/2\) 计算
代码:
#include<bits/stdc++.h>
using namespace std;
int N;
int main() {
while(scanf("%d",&N) != EOF) {
if(N%2 == 0) {
printf("%d\n",N/2*(N/2-1));
}
else {
int k = N/2;
printf("%d\n",k*(k-1)/2+k*(k+1)/2);
}
}
return 0;
}
B - Ehab and the Expected XOR Problem
题意:对于n,x要求我们给出一个数组a该数组内任意一段的异或都不为0和x,并且让这个数组长度尽量长,(数组元素满足\([1.2^n)\) 的范围,(1≤n≤18, 1≤x<2^18)
思路:我们考虑这个数组的前缀异或和S(n);我们可以得到这样一个性质S(i)^S(j) = a(j+1)a(j+2)....a(i) 也就是说我们任意一段的连续异或和都可以表示为他的前缀异或和的异或,那么问题就转化为我要构造一个S数组,其任意两个的异或和不为0或者x,我们知道a的范围满足\([1.2^n)\) 那么有异或的性质可知任意两个S不相等(相同的数异或和为0)且S的范围与a相同,那么我们分为两种情况,第一种\(x>=2^n\) 即无论我如何异或均不考虑x,那么输出即为[1.2^n)的所有数,第二种\(x<2^n\) 时我们就不能在S中找出m,n使得m^n = x ,我们知道这个东西对于每一个固定的m的n的解是唯一的所以可以直接扫一遍做m,n的vis输出即可
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = (1<<18)+5;
int n,x;
int S[maxn];
bool vis[maxn];
int main() {
scanf("%d%d",&n,&x);
int pre;
if(x>=(1<<n)) {
printf("%d\n",(1<<n) - 1);
int len = (1<<n);
for(int i = 1;i < len;i++) {
if(i == 1) {printf("%d ",i);pre = i;}
else {printf("%d ",pre^i);pre = i;}
}
}
else {
memset(vis,0,sizeof(vis));
printf("%d\n",((1<<n) - 1) / 2 );
int len = (1<<n) - 1;
int cnt = 0;
for(int i = 1;i <= len;i++) {
if(!vis[i] && i != x) {
S[cnt++] = i;
vis[i] = true;
int b = i^x;
vis[b] = true;
}
}
for(int i = 0;i < cnt;i++) {
if(i == 0) {printf("%d ",S[i]);pre = S[i];}
else {printf("%d ",pre^S[i]);pre = S[i];}
}
}
return 0;
}
C - The LCMs Must be Large
题意:对于一个长为n的常数数组,我们有m天,每m天取不同的下标的数做一次LCM, 要求判断是否存在这样的数组使得我们每次取出的LCM都大于剩下的数的LCM
思路:考虑到LCM是一个很取决于其中最大的一个数的函数,我们只要能保证我们的m天中任意两天我们都能够找到有至少一个公共元素,我们总可以去取一个很大的数使得其大于剩余元素的LCM且不矛盾,所以直接\(O(m^2n)\) 暴力判断即可
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxm = 55;
const int maxn = 1e4+5;
int m,n;
bool vis[maxm][maxn];
int main() {
scanf("%d%d",&m,&n);
int s,x;
memset(vis,0,sizeof(vis));
for(int i = 0;i < m;i++) {
scanf("%d",&s);
for(int j = 0;j < s;j++) {
scanf("%d",&x);
vis[i][x] = true;
}
}
bool flag;
for(int i = 0;i < m;i++) {
for(int j = 0;j < m;j++) {
if(i != j) {
flag = false;
for(int k = 1;k <= n && !flag;k++) {
if(vis[i][k]&&vis[j][k]) flag = true;
}
}
if(!flag) {printf("impossible\n");return 0;}
}
}
printf("possible\n");
return 0;
}
D - Frog and Portal
题意:有0-200个荷叶,有一只青蛙起始位于标号0的荷叶上,我们很任意知道青蛙到荷叶的方案数满足斐波那契数列,现在我们给出一个m代表到200的方案数,让我们通过添加传送门来达到这个方案数,输出传送门的数量和起点终点。
思路:先考虑200之前的几个位置到200 的方案数,例如199到200为1,198为2,197为3,196为5.....显然也是满足斐波那契数列的,我们考虑一个m为7 的情况,我们知道7可以由斐波那契数列中的5+2得到,即196和198两个位置,现在我们通过构造前面的传送门来,我们通过奇数传送门构造,这里我们就取前两个奇数1,3,但是我们要注意我们总的方案数还要加上到1,3 的方案,所以我们可以在4处放一个到自己的传送门,这样就使得到1,3只有一种方案,以此类推我们可以得到所有情况(我们可以大概知道一个数总可以由几个斐波那契数列的和组成)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll M;
ll fib[205];
ll ans[205];
void init() {
fib[200] = fib[199] = 1;
for(int i = 198;i >= 150;i--) fib[i] = fib[i+1] + fib[i+2];
}
int main() {
init();
int cnt;
while(scanf("%lld",&M) != EOF) {
if(M == 0) {
printf("2\n1 1\n2 1\n");
}
else {
cnt = 0;
for(int i = 150;i<=200;i++) {
if(M==0) break;
if(M - fib[i]>=0) {
M-=fib[i];
ans[cnt++] = i;
}
}
printf("%d\n",cnt+1);
for(int i = 0;i < cnt;i++) {
printf("%d %lld\n",2*i + 1,ans[i]);
}
printf("%d %d\n",2*cnt,2*cnt);
}
}
return 0;
}
F - The minimal unique substring
题意:给出n,k要求求出一个长度为n的01串其唯一字串的最小长度为k
思路:首先我们先观察三个串 1010,110110,11101110,答案都是01部分,我们可以下一个结论,形如 abab ( a 中有非负整数个 1 , b 中只有一个 0 )这类的字符串答案恒为 2 ,也就是 k==2 ,然后就是用这类字符串去构造出我们所需的 k 。我们可以尝试从末尾加一个1,那么之前的串变成了 10101,1101101,111011101,那么答案为010部分。我们可以发现,通过我们末尾添加的1,导致之前部分的 01 与我们末尾添加的1与前面一个0构成的 01 重复,使得之前的部分向后挪一位。于是,我们可以用这一规律去构造出我们想要的k,显然答案就是末尾部分的01(部分111...10111...10111)满足 0 的个数加 1 的个数等于 k-1 ,那么对中间的影响(部分111...1011111...110111)往后挪一位也就是我们的答案 k ,最后就是算出这形如 abab 字符串 a 部分中的 1 的个数有多少就行了,设 x 为 a 中 1 的个数,方程为 2*x+1+k-1=n ,化简为 x=(n-k)/2 ,根据题意 n 与 k 同奇偶,那么 x 也是唯一确定的,最后构造也由此生成。
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int n,k;
int x = (n - k) / 2;
cin>>n>>k;
for(int i=0,j=(n-k)/2;i<n;i++){
if(j) cout<<1,j--;
else cout<<0,j=(n-k)/2;
}
return 0;
}
G - Wrong Answer
题意:对一组数考虑一个区间和*区间长度的最大值,给出一个k,使得真正的最大值和
function find_answer(n, a)
# Assumes n is an integer between 1 and 2000, inclusive
# Assumes a is a list containing n integers: a[0], a[1], ..., a[n-1]
res = 0
cur = 0
k = -1
for i = 0 to i = n-1
cur = cur + a[i]
if cur < 0
cur = 0
k = i
res = max(res, (i-k)*cur)
return res
的做法求出的最大值差为k,构造这样的一组数(1≤n≤2000 and |a|≤1e6.k<=1e9)
思路:我们知道数组长度最大为2000,那么我们就来构造一个长度为2000的数组,其前1998均为0,最后两位为y<0,x>0,那么要满足2000(y+x) =k+x,即这两个答案之间差k,则y=(k-1999x)/2000,要使得y<0,则1e9-1999x为负数,那么我们可以取x=1e6-k%2000;即可
#include<bits/stdc++.h>
using namespace std;
int main() {
int k;
scanf("%d",&k);
printf("2000\n");
for(int i = 1;i<=1998;i++) printf("0 ");
int x = 1e6 - k%2000;
int y = (k - 1999*x) / 2000;
printf("%d %d",y,x);
}