A. Diana and Liana
首先如果一定无解,特判一下。那么我们考虑找恰好满足满足题目中的要求的区间,那么需要要删去的数一定是。前面的表示要把区间删成的倍数,后面的表示要把这个区间删到长度以内。判断这个值是否不大于能够删的最大值,然后更新答案。
不能枚举,但是当增大,恰好满足条件的区间的一定也会增大。那么就枚举左端点,右端点向右移动就行了。
CODE
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500005;
int m, n, k, s, a[MAXN], b[MAXN], mx;
int cnt[MAXN], need[MAXN], now, all, sum;
int main () {
scanf("%d%d%d%d", &m, &k, &n, &s); mx = m-n*k;
if(s > k) return puts("-1"), 0;
for(int i = 1; i <= m; ++i) scanf("%d", &a[i]);
for(int i = 1; i <= s; ++i) {
scanf("%d", &b[i]);
if(++need[b[i]] == 1) ++all;
}
int r = 0;
for(int i = 1; i <= m && r <= m; ++i) {
int tmp = (i-1)%k;
while(now < all && r < m) {
++cnt[a[++r]];
if(cnt[a[r]] == need[a[r]]) ++now;
}
if(now == all && tmp + max(r-i+1-k, 0) <= mx) {
printf("%d
", tmp + max(r-i+1-k, 0));
for(int j = 1; j <= tmp; ++j)
printf("%d ", j);
int left = max(r-i+1-k, 0);
for(int j = i; j <= r && left; ++j)
if(cnt[a[j]] > need[a[j]]) {
--left; --cnt[a[j]];
printf("%d ", j);
}
return 0;
}
if(--cnt[a[i]] == need[a[i]]-1) --now;
}
puts("-1");
}
B. Once in a casino
直接从左到右考虑,假设前面的位置都已经满足了,那么这位上的数需要加几/减几就直接加/减上去,但是这样有可能让下一位加到9以上或者减到0以下,暂时先不管。如果到最后就无解,反之一定有解(显然)。然后因为只要求输出前次,模拟操作即可:直接从左到右贪心执行操作,遇到越界的情况就递归就行了。
代码挺丑的,能过就行。
CODE
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, A[MAXN], cur;
long long ans;
char a[MAXN], b[MAXN];
inline int ff(int x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }
inline bool chkout(int x) { return !(x >= 0 && x <= 9); }
void solve(int i, int val) { //solve(i,val)表示把i加上val
if(cur == 100000) return;
//if(i > n) return;
int k = ff(val);
if(chkout(A[i+1]+val)) {
if(A[i+1]+val > 9) solve(i+1, 9-(A[i+1]+val));
else solve(i+1, 0-(A[i+1]+val));
}
if(cur == 100000) return;
while(val) {
++cur;
printf("%d %d
", i, k);
val -= k, A[i] += k, A[i+1] += k;
if(cur == 100000) return;
}
}
int main () {
scanf("%d", &n);
scanf("%s", a+1);
scanf("%s", b+1);
for(int i = 1; i <= n; ++i)
A[i] = a[i]-'0';
for(int i = 1; i <= n; ++i) {
A[i+1] += b[i]-'0'-A[i];
ans += abs(b[i]-'0'-A[i]);
A[i] = b[i]-'0';
}
if(A[n+1]) puts("-1");
else {
printf("%I64d
", ans);
for(int i = 1; i <= n; ++i)
A[i] = a[i]-'0';
for(int i = 1; i <= n && cur < 100000; ++i)
solve(i, b[i]-'0'-A[i]);
}
}
C. Compress String
直接预处理出来每两个位置的后缀的最长公共前缀记作,因为是,就行了(也可以)。
定义,表示到前个最小费用,转移很显然:
也有树/的算法
CODE
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
int n, a, b, dp[MAXN], mx[MAXN][MAXN], f[MAXN];
char s[MAXN];
int main () {
scanf("%d%d%d", &n, &a, &b);
scanf("%s", s+1);
for(int i = n; i; --i) {
for(int j = n; j; --j) {
if(s[i] == s[j]) mx[i][j] = mx[i+1][j+1] + 1;
f[i] = max(f[i], min(mx[i][j], i-j));
}
}
dp[1] = a;
for(int i = 2; i <= n; ++i) {
dp[i] = dp[i-1] + a;
for(int j = 2; j <= i; ++j) {
if(f[j] >= i-j+1)
dp[i] = min(dp[i], dp[j-1] + b);
}
}
printf("%d
", dp[n]);
}
D. Power Tree
树形
定义表示子树内节点全部统一成一个值且此值可控
定义表示子树内节点全部统一成一个值且此值不可控
转移如下:
答案就是
要求哪些可能被占领就再dfs一遍就行了。我的写法记忆化了一下。
总时间复杂度
CODE
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int n, c[MAXN];
vector<int>G[MAXN];
long long f[MAXN][2];
void dfs(int u, int ff) {
if(G[u].size() == 1 && u != 1) { //注意判根
f[u][0] = c[u];
f[u][1] = 0;
return;
}
long long tmp = 1ll<<60, sum = 0;
for(auto v : G[u])
if(v != ff) {
dfs(v, u);
sum += f[v][0];
tmp = min(tmp, f[v][1]-f[v][0]);
}
//printf("%I64d
", sum);
f[u][1] = tmp + sum;
f[u][0] = min(sum, f[u][1] + c[u]);
}
bool can[MAXN], vis[MAXN][2];
void dfs2(int u, int tp, int ff) {
if(vis[u][tp]) return; //记忆化
vis[u][tp] = 1;
if(G[u].size() == 1 && u != 1) { //注意判根
if(tp == 0)
can[u] = 1;
return;
}
long long tmp = 1ll<<60, sum = 0;
for(auto v : G[u])
if(v != ff) {
sum += f[v][0];
tmp = min(tmp, f[v][1]-f[v][0]);
}
//f[u][1] = tmp + sum;
//f[u][0] = min(sum, f[u][1] + c[u]);
if(tp == 0) {
if(f[u][0] == f[u][1] + c[u]) {
can[u] = 1;
int cnt = 0; //如果 cnt>1 两个儿子都可能被选中成为f[w][1], 那么大家都可能被选作f[v][0]
for(auto v : G[u])
if(v != ff) if(f[v][1]-f[v][0] == tmp) dfs2(v, 1, u), ++cnt;
for(auto v : G[u])
if(v != ff && (cnt > 1 || f[v][1]-f[v][0] != tmp))
dfs2(v, 0, u);
}
if(f[u][0] == sum) {
for(auto v : G[u])
if(v != ff) dfs2(v, 0, u);
}
}
else {
int cnt = 0;
for(auto v : G[u])
if(v != ff) if(f[v][1]-f[v][0] == tmp) dfs2(v, 1, u), ++cnt;
for(auto v : G[u])
if(v != ff && (cnt > 1 || f[v][1]-f[v][0] != tmp))
dfs2(v, 0, u);
}
}
int main () {
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &c[i]);
for(int i = 1, x, y; i < n; ++i)
scanf("%d%d", &x, &y),
G[x].push_back(y),
G[y].push_back(x);
dfs(1, -1);
printf("%I64d ", f[1][0]);
dfs2(1, 0, -1);
int ans = 0;
for(int i = 1; i <= n; ++i)
if(can[i]) ++ans;
printf("%d
", ans);
for(int i = 1; i <= n; ++i)
if(can[i]) printf("%d ", i);
}
E. The very same Munchhausen
考虑是否能拆位处理:
由低位到高位枚举,设表示枚举到第位,前位构成的数后累积到当前位的需要加上的值为,前位构成的数的数位之和与前个数位之和的差为,是否所有数位均为时的状态是否合法。
表示当前状态合法,表示不合法。
观察转移可以发现:
这一维可以省略
求解可以转成枚举当前位向外
答案状态即,为输出答案,记录表示最高位的值(),三元组表示状态的前驱。
计算得到
而(并不会证明),从而保证了复杂度。
CODE
#include <bits/stdc++.h>
using namespace std;
const int K = 1000;
struct node { int x, y, z; }pre[1005][2005][2];
queue<node>q;
int a, dig[1005][2005][2];
bool vis[1005][2005][2];
inline string solve() {
vis[0][K][0] = 1;
dig[0][K][0] = -1;
q.push({0, K, 0});
while(!q.empty()) {
int i = q.front().x, j = q.front().y, tp = q.front().z;
if(i == 0 && j == K && tp == 1) {
string re = ""; node tmp;
while(~dig[i][j][tp]) {
if(!re.empty() || dig[i][j][tp])
re += dig[i][j][tp]+'0';
tmp = pre[i][j][tp];
i = tmp.x, j = tmp.y, tp = tmp.z;
}
return re;
}
for(int k = 0; k < 10; ++k) {
int x = (i + k * a) / 10;
int y = (i + k * a) % 10 * a + j - k;
int z = (tp || k);
if(y >= 0 && y <= 2000 && !vis[x][y][z])
vis[x][y][z] = 1, dig[x][y][z] = k, pre[x][y][z] = q.front(), q.push({x, y, z});
}
q.pop();
}
return "-1";
}
int main () {
scanf("%d", &a);
cout<<solve()<<endl;
}
F. Secret Letters
实在无力写了,自己看这里吧
CODE
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, c, d, t[MAXN], tp[MAXN];
int main () {
scanf("%d%d%d", &n, &c, &d);
char ss[2];
for(int i = 0; i < n; ++i) {
scanf("%d%s", &t[i], ss);
tp[i] = ss[0] == 'P' ? 0 : 1;
}
scanf("%d", &t[n]);
int last; tp[n] = -1;
long long s = 0, ans = 1ll * n * d;
for(int i = n; i; --i) {
if(tp[i-1] != tp[i]) last = t[i]; //i=n 时一定会执行这一条, 把last设为t[n]
else s += min(1ll*d, 1ll*(last-t[i])*c);
ans = min(ans, s + 1ll * (i-1) * d + 1ll*(t[n]-t[i-1]) * c);
//s指后面i...n的代价 (i-1)d 指 0...i-2 直接送的代价 后面这一坨是i-1贯穿整过程的代价
}
printf("%I64d
", ans);
}