Codeforces Round #592 (Div. 2)
A - Pens and Pencils
题意:有a堂作文课和b次练习课,每1支钢笔可以用c堂作文课,每1支铅笔可以用d堂练习课。求携带笔不超过k支的方案。
题解:签到题,就要大大方方地签。不搞这么多绕来绕去的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void test_case() {
int a, b, c, d, k;
scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
int ac = (a + (c - 1)) / c, ad = (b + (d - 1)) / d;
if(ac + ad > k)
puts("-1");
else
printf("%d %d\n", ac, ad);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
B - Rooms and Staircases
题意:有两层楼的房间,[1,n]排一列,每次可以左右走,某些位置有楼梯。在不经过一个房间两次的前提下任选起点和路径,求最大经过的房间。
题解:又签到,很明显是从一个尽头走到最远的楼梯上楼再回去,枚举两个尽头。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[1005];
void test_case() {
int n;
scanf("%d%s", &n, s + 1);
int l = 1, r = n;
while(l <= n) {
if(s[l] == '0')
++l;
else
break;
}
while(r >= 1) {
if(s[r] == '0')
--r;
else
break;
}
if(l > r)
printf("%d\n", n);
else
printf("%d\n", 2 * n - 2 * min(n - r, l - 1));
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
C - The Football Season
题意:有n场球,一个队得了p分,已知赢一次得w分,平一次得d分,输不得分,且w>d。求一个解,无解输出-1。
题解:不就是求xw+yd=p的解,然后要求x+y<=n吗?套个扩展欧几里得求出最小的非负x,显然最小的非负x不能再减小,而减小y必定使得x增大,由于w>d所以要使得得分保持不变y减少x增量会少一些。这就复杂了,反过来解这个方程,则xd+yw=p。
注:这个题让我发现我模板里解线性同余方程会溢出longlong的bug,在不能使用int128的情况下很麻烦。通过一些分析我修复了这个问题,常数增大了一点,但是不容易溢出。具体详见模板。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {
if(b == 0)
return a;
while(ll t = a % b)
a = b, b = t;
return b;
}
ll ex_gcd(ll a, ll b, ll& x, ll& y) {
if(b == 0) {
x = 1, y = 0;
return a;
}
ll d = ex_gcd(b, a % b, x, y), t;
t = x, x = y, y = t - a / b * y;
return d;
}
//解线性同余方程 ax + by = c ,无解返回false
bool _LCE(ll a, ll b, ll c, ll &x0, ll &y0) {
ll x, y, d = ex_gcd(a, b, x, y);
if(c % d)
return false;
ll k = b / gcd(a, b);
x0 = ((x % k) * (c / d % k) % k + k) % k;
y0 = (c - a * x0) / b;
//x0是x的最小非负整数解
//x=x0+b*t,y=y0-a*t,是方程的所有解,对所有整数t成立
return true;
}
//解线性同余方程 ax = b mod n ,无解返回false
//和方程 ax + ny = b 等价
bool LCE(ll a, ll b, ll n, ll &x0) {
ll x, y;
if(_LCE(a, n, b, x, y)) {
ll k = n / gcd(a, n);
x0 = (x % k + k) % k;
//x0是最小非负整数解
//x=x0+k*t,是方程的所有解,对所有整数t成立
return true;
} else
return false;
}
void test_case() {
ll n, p, w, d, x, y, z = -1;
scanf("%lld%lld%lld%lld", &n, &p, &w, &d);
if(_LCE(d, w, p, y, x))
z = n - x - y;
if(x >= 0 && z >= 0)
printf("%lld %lld %lld\n", x, y, z);
else
puts("-1");
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
//scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
但是这个太草了,其实只需要枚举一个数,另一个数直接除下去就可以。
void test_case() {
ll n, p, w, d;
scanf("%lld%lld%lld%lld", &n, &p, &w, &d);
for(ll x = 0; x * w <= p; ++x) {
if((p - x * w) % d)
continue;
ll y = (p - x * w) / d;
ll z = n - x - y;
if(z >= 0) {
printf("%lld %lld %lld", x, y, z);
return;
}
}
puts("-1");
return;
}
这样会T,都不看范围的?其实是经过严密的观察发现,w个d和d个w是等价的,而把w个d换成d个w可以节省n,多出来的丢给z,所以y只有[0,m-1]这样的取值。还是封装一个带mod的大数比较靠谱。
void test_case() {
ll n, p, w, d;
scanf("%lld%lld%lld%lld", &n, &p, &w, &d);
for(ll y = 0; y < w && y * d <= p; ++y) {
if((p - y * d) % w)
continue;
ll x = (p - y * d) / w;
ll z = n - x - y;
if(z >= 0) {
printf("%lld %lld %lld", x, y, z);
return;
}
}
puts("-1");
return;
}
D - Paint the Tree
树形dp?求每个点染成某色的最小代价?但是还和孙子节点有关,想到这里的时候就发现dp要记录孙子的两个颜色,假如孙子有两个颜色那么儿子的颜色就确定了,但是这个点本身就没办法涂了,所以孙子只能有1种颜色。
题意:给一棵树染3种色其中之一,使得任意一条长为2的简单路径上的3个点的颜色都不同。这样就要求树没有分叉,否则有一个点度为3,那么不可能涂色完成。那么就是给一条链涂色,随便dp一下。
注意一定要分清楚换名之后的id和原id,以及现在在操作哪个id。
int a[100005][3];
int deg[100005];
int id[100005];
int aid[100005];
vector<int> G[100005];
ll dp[100005][3][3];
int cnt = 0;
void dfs(int u, int p) {
id[u] = ++cnt;
aid[cnt] = u;
for(auto &v : G[u]) {
if(v == p)
continue;
dfs(v, u);
}
}
int op(int i, int j) {
if(i == 0)
return j == 1 ? 2 : 1;
if(i == 1)
return j == 0 ? 2 : 0;
return j == 0 ? 1 : 0;
}
int ans[100005];
void test_case() {
int n;
scanf("%d", &n);
rep(i, 1, n) scanf("%d", &a[i][0]);
rep(i, 1, n) scanf("%d", &a[i][1]);
rep(i, 1, n) scanf("%d", &a[i][2]);
rep(i, 1, n - 1) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
++deg[u], ++deg[v];
if(deg[u] >= 3 || deg[v] >= 3) {
puts("-1");
return;
}
}
int root = -1;
rep(i, 1, n) {
if(deg[i] == 1) {
root = i;
break;
}
}
dfs(root, -1);
memset(dp, INF, sizeof(dp));
for(int i = 0; i <= 2; ++i) {
for(int j = 0; j <= 2; ++j) {
if(i == j)
continue;
dp[2][i][j] = a[aid[1]][i] + a[aid[2]][j];
}
}
rep(k, 3, n) {
for(int i = 0; i <= 2; ++i) {
for(int j = 0; j <= 2; ++j) {
if(i == j)
continue;
dp[k][i][j] = dp[k - 1][op(i, j)][i] + a[aid[k]][j];
}
}
}
ll ANS = 1ll * INF * INF;
int c1, c2;
for(int i = 0; i <= 2; ++i) {
for(int j = 0; j <= 2; ++j) {
if(i == j)
continue;
if(dp[n][i][j] < ANS) {
ANS = dp[n][i][j];
c1 = j;
c2 = i;
}
}
}
printf("%lld\n", ANS);
ans[n] = c1;
ans[n - 1] = c2;
per(i, n - 2, 1) ans[i] = op(ans[i + 1], ans[i + 2]);
rep(i, 1, n) printf("%d%c", ans[id[i]] + 1, " \n"[i == n]);
return;
}
E - Minimizing Difference
题意:给一个序列,每次操作可以选一个元素+1或者选一个元素-1,求不超过k次操作后生成的最小差。
题解:数据小的话可以贪心,每次选最大和最小里面出现次数较少的那个集体变化。这样每次操作会合并至少一个元素或者把k消耗到几乎殆尽。假如我二分一个极差k,然后尺取移动这个极差就可以转移出需要的值,所以二分+尺取是对的,先写这个解。
朴素的二分+尺取没有办法解决最后的边界不出现在ai中的情形。
作出一个假设,最优解的其中一种构造必定以ai为其中之一边界。这个结论看起来就很显然!得到这个结论之后需要进行正反两次check。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
#define rep(i,p1,p2) for(int i=(p1);i<=(p2);++i)
#define per(i,p1,p2) for(int i=(p1);i>=(p2);--i)
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int qpow(ll x, ll n) {
ll res = 1;
while(n) {
if(n & 1)
res = res * x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}
const int MAXN = 2e5;
/*---*/
int n;
ll k;
int a[100005];
ll prefix[100005];
ll suffix[100005];
int a2[100005];
ll prefix2[100005];
ll suffix2[100005];
bool check(ll d) {
ll cur = 0;
int i = 1, j = 1;
while(j + 1 <= n && a[j + 1] - a[i] <= d)
++j;
cur = prefix[i] + suffix[j + 1];
if(j + 1 <= n)
cur += (n - j) * (a[j + 1] - (a[i] + d));
if(cur <= k)
return true;
while(j < n) {
++i;
while(j + 1 <= n && a[j + 1] - a[i] <= d)
++j;
cur = prefix[i] + suffix[j + 1];
if(j + 1 <= n)
cur += (n - j) * (a[j + 1] - (a[i] + d));
if(cur <= k)
return true;
}
cur = 0;
i = 1, j = 1;
while(j + 1 <= n && a2[i] - a2[j + 1] <= d)
++j;
cur = prefix2[i] + suffix2[j + 1];
if(j + 1 <= n)
cur += (n - j) * ((a2[i] - d) - a2[j + 1]);
if(cur <= k)
return true;
while(j < n) {
++i;
while(j + 1 <= n && a2[i] - a2[j + 1] <= d)
++j;
cur = prefix2[i] + suffix2[j + 1];
if(j + 1 <= n)
cur += (n - j) * ((a2[i] - d) - a2[j + 1]);
if(cur <= k)
return true;
}
return false;
}
ll bs() {
ll L = 0, R = a[n] - a[1];
while(1) {
ll M = (L + R) >> 1;
if(L == M) {
if(check(L))
return L;
return R;
}
if(check(M))
R = M;
else
L = M + 1;
}
}
void test_case() {
scanf("%d%lld", &n, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; ++i)
prefix[i] = prefix[i - 1] + 1ll * (i - 1) * (a[i] - a[i - 1]);
for(int i = n; i >= 1; --i)
suffix[i] = suffix[i + 1] + 1ll * (n - i) * (a[i + 1] - a[i]);
for(int i = 1; i <= n; ++i)
a2[i] = a[n - i + 1];
for(int i = 1; i <= n; ++i)
prefix2[i] = prefix2[i - 1] + 1ll * (i - 1) * (a2[i - 1] - a2[i]);
for(int i = n; i >= 1; --i)
suffix2[i] = suffix2[i + 1] + 1ll * (n - i) * (a2[i] - a2[i + 1]);
printf("%lld\n", bs());
}
/*---*/
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
//scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
/*
1. 小数据问题退化:
输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
2. 内存:
内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
多组数据有进行初始化吗?memset会不会翻车?
3. 算术溢出:
乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
4. 习惯:
函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
id和aid是不是没有考虑,是不是弄反了?操作的是换名之后的,输出时应该是换名之前的。
5. 其他bug:
基环树是树加环,不是链加环。
*/
的确直接贪心推进边界是最简单的,每次移动要么合并多一个元素,要么把k消去一大截,使得不再能推进这个边界。
int n;
ll k;
int a[100005];
void test_case() {
scanf("%d%lld", &n, &k);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
sort(a + 1, a + 1 + n);
int L = 1, R = n;
int cntL = 1, cntR = 1;
int curL = a[1], curR = a[n];
while(L + 1 <= R && a[L + 1] == curL) {
++cntL;
++L;
}
while(R - 1 >= L && a[R - 1] == curR) {
++cntR;
--R;
}
if(L == R) {
puts("0");
return;
}
while(L < R && k >= min(cntL, cntR)) {
if(cntL <= cntR) {
int d = min(k / cntL, 1ll * a[L + 1] - a[L]);
curL += d;
k -= 1ll * d * cntL;
while(L + 1 <= R && a[L + 1] == curL) {
++cntL;
++L;
}
} else {
int d = min(k / cntR, 1ll * a[R] - a[R - 1]);
curR -= d;
k -= 1ll * d * cntR;
while(R - 1 >= L && a[R - 1] == curR) {
++cntR;
--R;
}
}
}
printf("%d\n", curR - curL);
}
F - Chips
题意:围成一个环的n颗黑白球,每个球每个时刻后会发生变化:当且仅当其邻居都和它异色时自己会变色。所有的球是同时变色的。求k时刻后这个环的样子。
题解:假如是偶数个球且黑白间隔,那么只需要判断k的奇偶性就可以了。否则连续的两个以上同色球是不会变化的,会变化的只是若干条链,我称为“异色链”,比如:
BWBWBWB
异色链就是
WBWBW
注意到每时刻异色链的长度会缩短2。直接贪心。
不知道这样写对不对:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
#define rep(i,p1,p2) for(int i=(p1);i<=(p2);++i)
#define per(i,p1,p2) for(int i=(p1);i>=(p2);--i)
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int qpow(ll x, ll n) {
ll res = 1;
while(n) {
if(n & 1)
res = res * x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}
const int MAXN = 2e5;
/*---*/
int n, k;
char a[600005];
char b[600005];
void test_case() {
scanf("%d%lld%s", &n, &k, a + 1);
bool suc = 1;
for(int i = 2; i <= n; ++i) {
if(a[i] != a[i - 1]) {
suc = 0;
break;
}
}
if(suc) {
puts(a + 1);
return;
}
if(!(n & 1)) {
bool suc = 1;
for(int i = 2; i <= n; ++i) {
if(a[i] == a[i - 1]) {
suc = 0;
break;
}
}
if(suc) {
if(k & 1) {
for(int i = 1; i <= n; ++i)
printf("%c", "BW"[a[i] == 'B']);
putchar('\n');
return;
}
for(int i = 1; i <= n; ++i)
printf("%c", "BW"[a[i] == 'W']);
putchar('\n');
return;
}
}
for(int i = 1; i <= n; ++i)
a[i + n + n] = a[i + n] = a[i];
int L, beg;
for(int i = 2;; ++i) {
if(a[i] == a[i - 1]) {
L = i - 1;
beg = i - 1;
break;
}
}
int R = L + n - 1;
//printf("L=%d R=%d\n", L - beg + 1, R - beg + 1);
while(a[R] == a[L])
--R;
while(a[L + 1] == a[L])
++L;
++L;
/*此时[L,R]就是链的两端*/
while(L <= R) {
//printf("L=%d R=%d\n", L - beg + 1, R - beg + 1);
int M = L;
for(int i = L + 1; i <= R; ++i) {
if(a[i] != a[i - 1])
M = i;
else {
M = i - 2;
break;
}
}
if(M >= L) {
/*此时[L,M]为第一个异色链*/
if((M - L + 1) % 2 == 0) {
//printf("L=%d M=%d\n", L - beg + 1, M - beg + 1);
//偶数
int d = min(k, (M - L + 1) / 2);
int L2 = L, M2 = M;;
for(int i = L, u = 1; u <= d; ++u, ++i) {
a[i] = a[i - 1];
L2 = i + 1;
}
for(int i = M, u = 1; u <= d; ++u, --i) {
a[i] = a[i + 1];
M2 = i - 1;
}
if(k & 1) {
for(int i = L2; i <= M2; ++i)
a[i] = ("BW"[a[i] == 'B']);
}
} else {
//printf("L=%d M=%d\n", L - beg + 1, M - beg + 1);
int d = min(k, (M - L + 1 + 1) / 2);
int L2 = L, M2 = M;;
for(int i = L, u = 1; u <= d; ++u, ++i) {
a[i] = a[i - 1];
L2 = i + 1;
}
for(int i = M, u = 1; u <= d; ++u, --i) {
a[i] = a[i + 1];
M2 = i - 1;
}
if(k & 1) {
for(int i = L2; i <= M2; ++i)
a[i] = ("BW"[a[i] == 'B']);
}
}
L = M + 1;
} else {
++L;
}
while(L + 1 <= R && a[L + 1] == a[L])
++L;
++L;
/*for(int i = beg, u = 1; u <= n; ++u, ++i)
putchar(a[i]);
putchar('\n');*/
}
for(int i = beg, u = 1; u <= n; ++u, ++i) {
b[i] = a[i];
if(i - n >= 1)
b[i - n] = a[i];
}
for(int i = 1; i <= n; ++i)
putchar(b[i]);
putchar('\n');
}
/*---*/
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
//scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
/*
1. 小数据问题退化:
输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
2. 内存:
内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
多组数据有进行初始化吗?memset会不会翻车?
3. 算术溢出:
乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
4. 习惯:
函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
id和aid是不是没有考虑,是不是弄反了?操作的是换名之后的,输出时应该是换名之前的。
5. 其他bug:
基环树是树加环,不是链加环。
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
#define rep(i,p1,p2) for(int i=(p1);i<=(p2);++i)
#define per(i,p1,p2) for(int i=(p1);i>=(p2);--i)
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int qpow(ll x, ll n) {
ll res = 1;
while(n) {
if(n & 1)
res = res * x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}
const int MAXN = 2e5;
/*---*/
int n, k;
char a[400005];
void test_case() {
scanf("%d%d%s", &n, &k, a + 1);
bool suc = 1;
for(int i = 2; i <= n; ++i) {
if(a[i] != a[i - 1]) {
suc = 0;
break;
}
}
if(suc) {
puts(a + 1);
return;
}
if(n % 2 == 0) {
bool suc = 1;
for(int i = 2; i <= n; ++i) {
if(a[i] == a[i - 1]) {
suc = 0;
break;
}
}
if(suc) {
if(k & 1) {
for(int i = 1; i <= n; ++i)
printf("%c", "BW"[a[i] == 'B']);
putchar('\n');
return;
}
for(int i = 1; i <= n; ++i)
printf("%c", "BW"[a[i] == 'W']);
putchar('\n');
return;
}
}
for(int i = 1; i <= n; ++i)
a[i + n] = a[i];
int L, beg;
for(int i = 2;; ++i) {
if(a[i] == a[i - 1]) {
L = i - 1;
beg = i - 1;
break;
}
}
int R = L + n - 1;
while(a[R] == a[L])
--R;
while(a[L + 1] == a[L])
++L;
++L;
/*此时[L,R]就是链的两端*/
while(L <= R) {
int M = R;
for(int i = L + 1; i <= R; ++i) {
if(a[i] == a[i - 1]) {
M = i - 2;
break;
}
}
if(M >= L) {
/*此时[L,M]为第一个异色链*/
int d;
if((M - L + 1) % 2 == 0)
d = min(k, (M - L + 1) / 2);
else
d = min(k, (M - L + 1 + 1) / 2);
int L2 = L, M2 = M;;
for(int i = L, u = 1; u <= d; ++u, ++i) {
a[i] = a[i - 1];
L2 = i + 1;
}
for(int i = M, u = 1; u <= d; ++u, --i) {
a[i] = a[i + 1];
M2 = i - 1;
}
if(k & 1) {
for(int i = L2; i <= M2; ++i)
a[i] = ("BW"[a[i] == 'B']);
}
L = M + 1;
} else {
/*此时没有异色链*/
++L;
}
while(L + 1 <= R && a[L + 1] == a[L])
++L;
++L;
}
for(int i = beg, u = 1; u <= n; ++u, ++i) {
if(i - n >= 1)
a[i - n] = a[i];
}
for(int i = 1; i <= n; ++i)
putchar(a[i]);
putchar('\n');
}
/*---*/
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
//scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
/*
1. 小数据问题退化:
输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
2. 内存:
内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
多组数据有进行初始化吗?memset会不会翻车?
3. 算术溢出:
乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
4. 习惯:
函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
id和aid是不是没有考虑,是不是弄反了?操作的是换名之后的,输出时应该是换名之前的。
5. 其他bug:
基环树是树加环,不是链加环。
*/
G - Running in Pairs
题意:给两个排列,调整他们的位置使得他们对应位置的最大值的和最大,且这个值不能超过k。
题解:最小的肯定是同向,最大的肯定是反向。但是怎么调整呢?一开始全部设为同向,然后发现首尾对称位置交换会使得答案变大,且能最节约这个最大的数字,当序列长度为奇数时,每次交换得到都是偶数,且这个收益逐渐减少到2,最后可能可以进行一次临位交换有可能把答案扩大1(仅当本身中部是正序时)。当序列的长度为偶数时,首尾交换每次都是奇数,且收益减少到1,显然这样可以凑出任意一个自然数。(错,不能构造出2,估计很多人也要FST在这里了,2要通过其他方法构造出来)
1 2 3 4
1 2 3 4
偶数个的情况,截取中间的四个如上,因为外面已经处理过+5了,所以现在只需要考虑<=4的情况。
要构造4,和上面说的一样,+3+1:swap(1,4),swap(2,3)
要构造3,和上面说的一样,+3:swap(1,4)
要构造2,+2:swap(2,4)
要构造1,和上面说的一样,+1:swap(2,3)
大的思路是对的,细节不行,以后在边界(这里的边界是中部数据较小时)要格外注意。
int n;
ll k;
int a[1000005];
int b[1000005];
void test_case() {
scanf("%d%lld", &n, &k);
if(n == 1) {
printf("1\n1\n1\n");
return;
}
if(n == 2) {
if(k < 3)
printf("-1\n");
else if(k == 3)
printf("3\n1 2\n1 2\n");
else
printf("4\n1 2\n2 1\n");
return;
}
ll sum = 0;
for(int i = 1; i <= n; ++i) {
a[i] = i;
b[i] = i;
sum += i;
}
if(k < sum) {
puts("-1");
return;
}
int mid = (n + 1) / 2;
if(n % 2 == 1) {
for(int i = 1; i <= n / 2; ++i) {
ll d = (a[n - i + 1] - a[i]);
if(sum + d <= k) {
swap(b[i], b[n - i + 1]);
sum += d;
}
}
if(sum + 1 <= k) {
if(b[mid - 1] < b[mid]) {
swap(a[mid - 1], a[mid]);
++sum;
}
}
} else {
for(int i = 1; i <= (n - 4) / 2; ++i) {
ll d = (a[n - i + 1] - a[i]);
if(sum + d <= k) {
swap(b[i], b[n - i + 1]);
sum += d;
}
}
if(sum + 4 <= k) {
swap(b[mid], b[mid + 1]);
swap(b[mid - 1], b[mid + 2]);
sum += 4;
} else if(sum + 3 <= k) {
swap(b[mid - 1], b[mid + 2]);
sum += 3;
} else if(sum + 2 <= k) {
swap(b[mid], b[mid + 2]);
sum += 2;
} else if(sum + 1 <= k) {
swap(b[mid], b[mid + 1]);
sum += 1;
}
}
ll tsum = 0;
for(int i = 1; i <= n; ++i)
tsum += max(a[i], b[i]);
printf("%lld\n", tsum);
for(int i = 1; i <= n; ++i)
printf("%d%c", a[i], " \n"[i == n]);
for(int i = 1; i <= n; ++i)
printf("%d%c", b[i], " \n"[i == n]);
assert(tsum == sum);
}