A. Consecutive Sum
题目链接:Consecutive Sum
题意:
给定一长度为 n n n的序列 a a a,你可以进行如下操作(两步为一次操作)至多 k k k次 :
-
选择两数 i i i和 j j j,其中需满足 i ≡ j ( m o d k ) i\equiv j(mod\ k) i≡j(mod k)
-
交换 a i a_i ai和 a j a_j aj
操作完毕后,需从序列中选出一段长度为 k k k的连续子序列,求选出的连续子序列的数之和的最大值.
题解:
由于最后选出的连续子序列长度恰好为 k k k,因此序列下标除 k k k余数必定遍历完 0 , 1 , 2 , . . . , k − 1 {0, 1, 2,...,k-1} 0,1,2,...,k−1,且对于每一个余数 i i i,我们之多用一次操作便可以从 x % k = i x\% k=i x%k=i的 x x x中选出最大的一个并交换至 i i i的位置(若 i = 0 i=0 i=0则交换至 k k k),最终复杂度 O ( n ) \Omicron(n) O(n)
code
const ll N=120;
ll T;
ll mx[N];
ll n, k;
ll res;
int main() {
read(T);
while (T--) {
memset(mx, 0, sizeof mx);
read(n); read(k);
for (R ll i=1, num; i<=n; i++) {
read(num);
chkmax(mx[i%k], num);
}
res=0;
for (R ll i=0; i<k; i++) res+=mx[i];
writeln(res);
}
}
B. Rule of League
题目链接: Rule of League
题意:
有 n n n名选手参加一乒乓球锦标赛,选手编号从 1 1 1 到 n n n.比赛流程如下:
第一轮 1 1 1号与 2 2 2号先比,败者出局;胜者进入第二轮,与 3 3 3号比,败者出局;胜者进入第三轮与 4 4 4号比…以此类推,保证不存在平局的情况
现信息已知每名选手获胜次数均为 x x x或 y y y次,问是否存在符合该信息的比赛结果,若存在,输出 n − 1 n-1 n−1个数表示每场比赛胜者编号,否则输出 − 1 -1 −1.
题解:
注意到在第一轮比赛中 1 1 1和 2 2 2比必有一方出局,于是出局的那位胜场为 0 0 0.因此 x , y x, y x,y中必有且只有一个为 0 0 0,否则无解.
x , y x, y x,y中一项为 0 0 0之后,不妨假设 y = 0 y=0 y=0,设共有 a a a个人赢了 x x x次,剩下的人赢 y y y次也即没赢过,由于每次淘汰一个人,最终剩余一人,那么总比赛场数即 a ⋅ x = n − 1 a\cdot x=n-1 a⋅x=n−1,因此必须满足 x ∣ n − 1 x|n-1 x∣n−1,否则无解.
无解判定完后,开始构造方案数.从1号或者2号开始,计数器 c n t cnt cnt表示当前胜者胜利几场, n o w now now表示当前胜者是谁.从 1 1 1到 n − 1 n-1 n−1枚举比赛场数, c n t = x cnt=x cnt=x时将 n o w now now替换为当前场另一选手即可.复杂度 O ( n ) \Omicron(n) O(n)
code
const ll N=120;
ll T;
ll n, x, y;
int main() {
read(T);
while (T--) {
read(n); read(x); read(y);
if ((x+y==0) || (x*y!=0)) {
writeln(-1); continue;
}
if (x<y) swap(x, y);
if ((n-1)%x!=0) {
writeln(-1); continue;
}
for (R ll i=2, now=0, cnt=x; i<=n; i++) {
if (cnt==x) now=i, cnt=0;
writesp(now); ++cnt;
}
putchar('\n');
}
}
C. Parity Shuffle Sorting
题意:
给定一长度为 n n n的序列 a a a,你可以进行如下操作(一次操作包含两步)至多 n n n次,目标是使得该队列单调不降.
-
选定两下标 l < r l<r l<r
-
若 a l + a r a_l+a_r al+ar为奇数,则令 a r = a l a_r=a_l ar=al;若为偶数,则令 a l = a r a_l=a_r al=ar
值得注意的是,你无需最小化你的操作次数,仅需保证 n n n次操作内能达成目标即可.可以证明 n n n次内必定能完成目标,故你只需输出你的方案.
思路:
可以注意到,如果我们选定了 l l l和 r r r,那无论如何选,有一个结果是必然的,即 l l l和 r r r将会变成同一个值.这个性质既简单又关键.
利用这个性质我们可以先选定 1 1 1和 n n n,让 a 1 a_1 a1和 a n a_n an的值相等,这时有两种情况 :
-
a 1 a_1 a1和 a n a_n an均为奇数,那遍历 a i ( i = 2 , 3 , . . . n − 1 ) {a_i}(i=2,3,...n-1) ai(i=2,3,...n−1),若 a i a_i ai为奇数,那么有 a i + a n a_i+a_n ai+an为偶数,因此选定 i i i和 n n n可令 a i = a n a_i=a_n ai=an;若 a i a_i ai为偶数,则选定 1 1 1和 i i i可令 a i = a 1 a_i=a_1 ai=a1
-
a 1 a_1 a1和 a n a_n an均为偶数,同理分析即可.
运用该算法,我们至多修改 n − 1 n-1 n−1个数,且修改每个数均用 1 1 1次操作,故能满足题意.
code
const ll N=1e5+5;
ll T;
ll n, x, y;
ll num[N];
int main() {
read(T);
while (T--) {
read(n);
for (R ll i=1; i<=n; i++) read(num[i]);
writeln(n-1);
if (n==1) continue;
writesp(1); writeln(n);
if ((num[1]+num[n])&1) num[n]=num[1];
else num[1]=num[n];
for (R ll i=2; i<n; i++) {
if ((num[1]+num[i])&1) {
writesp(1); writeln(i);
}else {
writesp(i); writeln(n);
}
}
}
}
D1. Zero-One (Easy Version)
题意:
给定两个长度均为 n n n的 01 01 01序列 A A A和 B B B,你可以对序列 A A A进行若干次操作,操作描述如下:
选定两下标 l < r l<r l<r,使得 a l = 1 − a l , a r = 1 − a r a_l=1-a_l, a_r=1-a_r al=1−al,ar=1−ar.
其中每次操作都有一定花费,若 r = l + 1 r=l+1 r=l+1,则花费为 x x x,否则花费 y y y.保证 x ≥ y x\geq y x≥y.
试问能否通过操作使 A A A变成 B B B,若可以则输出最小花费,否则输出 − 1 -1 −1
思路:
先判断是否有解. 容易发现该操作一次改变两个数,不影响 01 01 01序列的异或和,故若两序列异或和不同,则无解.
然后是求最小花费.若有解,显然需要改变的位置数 n u m num num为偶数.
n u m = 2 num=2 num=2且需改变两数相邻时,不妨设其下标为 v , v + 1 v,v+1 v,v+1,若 x ≤ 2 y x\leq 2y x≤2y,则直接改变 v v v和 v + 1 v+1 v+1位置即可.但若 x > 2 y x>2y x>2y,改变一次相邻数不如改变两次间隔数划算,由于序列长至少为 5 5 5,所以我们一定能够找到一个位置 p o s pos pos,使得 p o s pos pos与 v , v + 1 v, v+1 v,v+1均不相邻.此时先改变 v v v和 p o s pos pos,再改变 v + 1 v+1 v+1和 p o s pos pos即可,代价为 2 y 2y 2y.
n u m = 2 num=2 num=2且两数不相邻,显然代价为 y y y
n u m ≥ 4 num\geq 4 num≥4时,不妨设需改变位置为 p 1 , p 2 , p 3 , p 4 , . . . , p 2 k − 1 , p 2 k p_1, p_2, p_3, p_4, ..., p_{2k-1}, p_{2k} p1,p2,p3,p4,...,p2k−1,p2k,采用贪心的策略,我们进行最少 k k k次操作,第 i i i次操作选择 p i p_{i} pi和 p k + i p_{k+i} pk+i即可保证每次操作选择两下标不相邻,代价为 y y y.最终最小代价即为 k ∗ y k*y k∗y
至此,所有情况讨论完毕.
const ll N=3e3+3;
ll T;
ll n, x, y;
string s1, s2;
bool s[N], goal[N];
bool sum1, sum2;
ll res;
int main() {
read(T);
while (T--) {
read(n); read(x); read(y);
cin>>s1>>s2;
for (R ll i=0; i<n; i++) {
s[i+1]=s1[i]-'0';
goal[i+1]=s2[i]-'0';
}
chkmin(x, y*2);
res=0;
for (R ll i=1; i<=n; i++) {
if (s[i]^goal[i]) ++res;
}
if (res&1) writeln(-1);
else if (res==0) writeln(0);
else if (res==2) {
for (R ll i=1; i<=n; i++) {
if (s[i]^goal[i]) {
if (s[i+1]^goal[i+1]) writeln(x);
else writeln(y);
break;
}
}
}
else {
writeln(res/2*y);
}
}
}