ZR19CSP-S赛前冲刺day7
为了保护ZR的版权,这里不提供题目QWQ
http://zhengruioi.com/contest/446(你进得去吗/xyx)
A 字符串
考虑DP,转移很容易
输出就按照转移的值输出就行了
#include<bits/stdc++.h>
#define N 5005
using namespace std;
int f[N][N], vis[N][N], nxtst0[N], nxtst1[N], nxtstt0[N], nxtstt1[N], n, m;
char st[N], stt[N];
int dfs(int i, int j) {
if(i > n + 1&& j > m + 1) return 0;
if(vis[i][j]) return f[i][j];
vis[i][j] = 1, f[i][j] = 0;
int x = dfs(nxtst0[i], nxtstt0[j]) + 1, y = dfs(nxtst1[i], nxtstt1[j]) + 1;
if(x < y) f[i][j] = x;
else f[i][j] = y;
return f[i][j];
}
void DFS(int i, int j) {
if(i > n + 1&& j > m + 1) return;
int x = dfs(nxtst0[i], nxtstt0[j]) + 1, y = dfs(nxtst1[i], nxtstt1[j]) + 1;
if(x <= y) printf("0"), DFS(nxtst0[i], nxtstt0[j]);
else printf("1"), DFS(nxtst1[i], nxtstt1[j]);
}
int main() {
scanf("%d%d", &n, &m);
scanf("%s", st + 1);
scanf("%s", stt + 1);
nxtst0[n + 1] = n + 2, nxtst1[n + 1] = n + 2;
nxtst0[n + 2] = n + 2, nxtst1[n + 2] = n + 2;
for(int i = n; i >= 1; i --) {
nxtst0[i] = nxtst0[i + 1], nxtst1[i] = nxtst1[i + 1];
if(st[i] == '0') nxtst0[i] = i + 1;
else nxtst1[i] = i + 1;
}
nxtstt0[m + 1] = m + 2, nxtstt1[m + 1] = m + 2;
nxtstt0[m + 2] = m + 2, nxtstt1[m + 2] = m + 2;
for(int i = m; i >= 1; i --) {
nxtstt0[i] = nxtstt0[i + 1], nxtstt1[i] = nxtstt1[i + 1];
if(stt[i] == '0') nxtstt0[i] = i + 1;
else nxtstt1[i] = i + 1;
}
dfs(1, 1);
DFS(1, 1);
return 0;
}
B 交换
显然最优形式就是每个数出现的次数是 2 i − 1 2^i - 1 2i−1次
考虑把一个数的出现次数要从 2 i − 1 2^i - 1 2i−1次加到 2 i + 1 − 1 2^{i + 1} - 1 2i+1−1次
这样答案就会变大1,代价就是那个数的大小乘以 2 i 2^i 2i
如果贪心做,每次拿代价最小的贡献答案只能拿部分分
考虑二分最大的代价M是多少,即把代价小于最大的代价的都拿了,看所需的代价和是否满足要求(<=N)
算代价那里可以log分治下去算,具体看代码
这样时间复杂度就是 O ( T l o g 2 2 N ) O(Tlog_2^2N) O(Tlog22N)的
代价等于M那部分要特殊处理,因为不一定代价等于M的都会拿完,要尽量拿
代码也很短
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int check(int x) {
return x? (x * (x + 1) / 2 + 2 * check(x / 2)) : 0;//算代价
}
int calc(int x) {
return x? (x + calc(x / 2)) : 0;//算答案
}
int n, t;
signed main() {
scanf("%lld", &t);
while(t --) {
scanf("%lld", &n);
int l = 0, r = 1e9;
while(l + 1 < r) {//二分最大的代价
int mid = (l + r) >> 1;
if(check(mid) <= n) l = mid;
else r = mid;
}
printf("%lld\n", calc(l) + (n - check(l)) / (l + 1));
}
return 0;
}
C 交换
这是道JOISC的原题啊QWQ
发现把一个数和相邻的交换后除了那个数,其他数的相对位置并没有变。
考虑一个数是丢前面还是丢后面
如果丢前面,拿前面小于等于它的数显然不和它交换更优,所以对答案的贡献是它前面比它大的个数
丢后面同理
然后发现这就是一个sb经典的二维偏序问题,直接拿树状数组维护一下就好了
code:
#include<bits/stdc++.h>
#define N 1000005
#define lowbit(x) (x & -x)
using namespace std;
int tree[N];
void update(int x, int y) {
for(; x < N; x += lowbit(x)) tree[x] += y;
}
int query(int x) {
int ret = 0;
for(; x; x -= lowbit(x)) ret += tree[x];
return ret;
}
int n, a[N], ha[N], haha[N];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++) {
ha[i] = query(N - 1 - a[i] - 1);
update(N - 1 - a[i], 1);
}
memset(tree, 0, sizeof tree);
for(int i = n; i >= 1; i --) {
haha[i] = query(N - 1 - a[i] - 1);
update(N - 1 - a[i], 1);
}
long long ans = 0;
for(int i = 1; i <= n; i ++) ans += min(ha[i], haha[i]);
printf("%lld", ans);
return 0;
}
总结
思维发散些QWQ