Codeforces Round #501 (Div. 3)
A - Points in Segments
题意:implement
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool vis[105];
int ans[105], atop;
void test_case() {
int n, m;
scanf("%d%d", &n, &m);
while(n--) {
int u, v;
scanf("%d%d", &u, &v);
for(int i = u; i <= v; ++i)
vis[i] = 1;
}
for(int i = 1; i <= m; ++i)
if(!vis[i])
ans[++atop] = i;
printf("%d\n", atop);
for(int i = 1; i <= atop; ++i)
printf("%d%c", ans[i], " \n"[i == atop]);
if(atop == 0)
puts("");
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
B - Obtaining the String
题意:给两个字符串s,t,使用冒泡排序从s到达t,求方法。
题解:implement
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[105], ss[105], t[105], tt[105];
int ans[10005], atop;
void test_case() {
int n;
scanf("%d%s%s", &n, s + 1, t + 1);
strcpy(ss + 1, s + 1);
strcpy(tt + 1, t + 1);
sort(ss + 1, ss + 1 + n);
sort(tt + 1, tt + 1 + n);
if(strcmp(ss + 1, tt + 1) != 0) {
puts("-1");
return;
}
for(int i = 1; i <= n; ++i) {
while(t[i] != s[i]) {
for(int j = i + 1; j <= n; ++j) {
if(s[j] == t[i]) {
int k = j - 1;
while(t[i] != s[i]) {
ans[++atop] = k;
swap(s[k], s[k + 1]);
--k;
}
break;
}
}
}
}
printf("%d\n", atop);
for(int i = 1; i <= atop; ++i)
printf("%d%c", ans[i], " \n"[i == atop]);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
C - Songs Compression
题意:有n个数字ai和n个数字bi,花费代价1可以把ai缩小到bi,求缩减总容量小于等于m的最小代价。
题解:implement
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
priority_queue<int> pq;
void test_case() {
int n, m;
scanf("%d%d", &n, &m);
ll sum = 0;
for(int i = 1, a, b; i <= n; ++i) {
scanf("%d%d", &a, &b);
pq.push(a - b);
sum += a;
}
int cnt = 0;
while(pq.size()) {
if(sum <= m)
break;
sum -= pq.top();
++cnt;
pq.pop();
}
if(sum > m)
cnt = -1;
printf("%d\n", cnt);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
D - Walking Between Houses
题意:有[1,n]号房子,相邻房子距离1,要移动恰好k次,总共恰好s距离,求方法。注意不能原地踏步。
题解:很显然只要是s>=k且s<=k*(n-1)都可以构造,但是因为不能原地踏步就很恶心。先尽可能走最远,直到某次走最远之后会使得s<k,这样就走一步不那么远的直到走完之后s=k,然后反复横跳。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int ans[200005], atop;
void test_case() {
int n, k;
ll s;
scanf("%d%d%lld", &n, &k, &s);
if(s < k || s > (1ll * k * (n - 1))) {
puts("NO");
return;
}
if(s == k) {
puts("YES");
for(int i = 1; i <= k; ++i)
printf("%d%c", 1 + ((i & 1) == 1), " \n"[i == k]);
return;
}
int cur = 1;
while(s - (n - 1) >= (k - 1)) {
s -= n - 1;
k -= 1;
if(cur == 1) {
ans[++atop] = n;
cur = n;
} else {
ans[++atop] = 1;
cur = 1;
}
}
if(k) {
int p = s - (k - 1);
assert(p <= (n - 1));
if(cur == 1) {
ans[++atop] = 1 + p;
cur = 1 + p;
s -= p;
k -= 1;
} else {
ans[++atop] = n - p;
cur = n - p;
s -= p;
k -= 1;
}
}
if(cur == 1) {
for(int i = 1; i <= k; ++i)
ans[++atop] = 1 + ((i & 1) == 1);
} else {
for(int i = 1; i <= k; ++i)
ans[++atop] = cur - ((i & 1) == 1);
}
puts("YES");
ll sum = 0;
ans[0] = 1;
for(int i = 1; i <= atop; ++i) {
printf("%d%c", ans[i], " \n"[i == atop]);
sum += abs(ans[i] - ans[i - 1]);
}
//printf("sum=%lld\n", sum);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
E1 - Stars Drawing (Easy Edition)
题意:给一个图,用一些十字星把这个图覆盖出来。图<=100*100。
题解:枚举每个中心,然后枚举上下左右拓展的极限,把这个极限覆盖上去,最后得出的图假如和原图不同就-1。
E2 - Stars Drawing (Hard Edition)
题意:图<=1000*1000。把枚举上下左右换成一个很复杂的check,输入一个中心、一个方向和一个长度,返回这段是否完全被覆盖。这个可以用前缀和来实现,所以枚举中心然后二分长度就可以。但是最后答案不能够暴力更新,这个就直接在lr打上差分标记,然后最后扫过来填上去就可以了。
有没有更好的办法?
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
char g[1005][1005];
int prefixl[1005][1005];
int prefixu[1005][1005];
int cntl[1005][1005], cntu[1005][1005];
char ans[1005][1005];
bool checku(int i, int j, int l) {
if(i - l - 1 < 0)
return false;
return (prefixu[i - 1][j] - prefixu[i - l - 1][j]) == l;
}
bool checkl(int i, int j, int l) {
if(j - l - 1 < 0)
return false;
return (prefixl[i][j - 1] - prefixl[i][j - l - 1]) == l;
}
bool checkr(int i, int j, int l) {
if(i + l > n)
return false;
return (prefixu[i + l][j] - prefixu[i][j]) == l;
}
bool checkd(int i, int j, int l) {
if(j + l > m)
return false;
return (prefixl[i][j + l] - prefixl[i][j]) == l;
}
bool check(int i, int j, int l) {
return checku(i, j, l) && checkl(i, j, l) && checkr(i, j, l) && checkd(i, j, l);
}
struct Answer {
int i, j, l;
Answer(int ii, int jj, int ll) {
i = ii, j = jj, l = ll;
}
};
vector<Answer>ans2;
void solve(int i, int j) {
if(i == 1 || j == 1 || i == n || j == m)
return;
if(g[i][j] == '*' && g[i - 1][j] == '*' && g[i + 1][j] == '*' && g[i][j - 1] == '*' && g[i][j + 1] == '*') {
int L = 1, R = min(min(i - 1, n - i), min(j - 1, m - j));
int ans = 0;
while(1) {
int M = L + R >> 1;
if(L == M) {
if(check(i, j, R)) {
ans = R;
break;
}
ans = L;
break;
}
if(check(i, j, M))
L = M;
else
R = M - 1;
}
++cntu[i - ans][j];
--cntu[i + ans + 1][j];
++cntl[i][j - ans];
--cntl[i][j + ans + 1];
ans2.push_back(Answer(i, j, ans));
}
return;
}
void test_case() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
scanf("%s", g[i] + 1);
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j)
prefixl[i][j] = prefixl[i][j - 1] + (g[i][j] == '*');
}
for(int j = 1; j <= m; ++j) {
for(int i = 1; i <= n; ++i)
prefixu[i][j] = prefixu[i - 1][j] + (g[i][j] == '*');
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j)
solve(i, j);
}
memset(ans, '.', sizeof(ans));
for(int i = 1; i <= n; ++i) {
int cur = 0;
for(int j = 1; j <= m; ++j) {
cur += cntl[i][j];
if(cur)
ans[i][j] = '*';
}
}
for(int j = 1; j <= m; ++j) {
int cur = 0;
for(int i = 1; i <= n; ++i) {
cur += cntu[i][j];
if(cur)
ans[i][j] = '*';
if(ans[i][j] != g[i][j]) {
puts("-1");
return;
}
}
}
printf("%d\n", (int)ans2.size());
for(auto i : ans2)
printf("%d %d %d\n", i.i, i.j, i.l);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
事实上并不需要二分,可以dp转移出四个方向上的最远值。然后直接取min。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
char g[1005][1005];
short u[1005][1005];
short d[1005][1005];
short l[1005][1005];
short r[1005][1005];
short cntu[1005][1005];
short cntl[1005][1005];
char ans[1005][1005];
struct Answer {
int i, j, l;
Answer(int ii, int jj, int ll) {
i = ii, j = jj, l = ll;
}
};
vector<Answer>ans2;
void solve(int i, int j) {
int len = min(min(u[i][j], d[i][j]), min(l[i][j], r[i][j]));
if(len <= 1)
return;
len -= 1;
++cntu[i - len][j];
--cntu[i + len + 1][j];
++cntl[i][j - len];
--cntl[i][j + len + 1];
ans2.push_back(Answer(i, j, len));
}
void test_case() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%s", g[i] + 1);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
u[i][j] = (g[i][j] == '*') ? u[i - 1][j] + 1 : 0;
l[i][j] = (g[i][j] == '*') ? l[i][j - 1] + 1 : 0;
}
}
for(int i = n; i >= 1; --i) {
for(int j = m; j >= 1; --j) {
d[i][j] = (g[i][j] == '*') ? d[i + 1][j] + 1 : 0;
r[i][j] = (g[i][j] == '*') ? r[i][j + 1] + 1 : 0;
}
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j)
solve(i, j);
}
memset(ans, '.', sizeof(ans));
for(int i = 1; i <= n; ++i) {
int cur = 0;
for(int j = 1; j <= m; ++j) {
cur += cntl[i][j];
if(cur)
ans[i][j] = '*';
}
}
for(int j = 1; j <= m; ++j) {
int cur = 0;
for(int i = 1; i <= n; ++i) {
cur += cntu[i][j];
if(cur)
ans[i][j] = '*';
if(ans[i][j] != g[i][j]) {
puts("-1");
return;
}
}
}
printf("%d\n", (int)ans2.size());
for(auto i : ans2)
printf("%d %d %d\n", i.i, i.j, i.l);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
F - Bracket Substring
题意:有个不一定合法的括号串s,求长度为2n的串中有多少种是包含n为子串且合法的。
题解:换句话说就是在左右加一个n个使得它合法,问有多少种。看起来像是dp。设dp[i][j][k]为已经有总共i长度的串,其中现在以s的第j位结尾,左括号比右括号多k个的合法方案数,则答案为dp[2n][l][0]。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
char s[205];
ll dp[205][205][205];
void test_case() {
int n;
scanf("%d%s", &n, s + 1);
int l = strlen(s + 1);
dp[0][0][0] = 1;
for(int i = 1; i <= 2 * n; ++i) {
//没有进入s串,直接转移
for(int k = 0; k <= i; ++k)
dp[i][0][k] = (k >= 1 ? dp[i - 1][0][k - 1] : 0) + dp[i - 1][0][k + 1];
//开始进入s串/已经进入s串
for(int j = 1; j <= min(i, l); ++j) {
if(s[j] == '(') {
for(int k = 0; k <= i; ++k)
dp[i][j][k] = (k >= 1 ? dp[i - 1][j - 1][k - 1] : 0);
} else {
for(int k = 0; k <= i; ++k)
dp[i][j][k] = dp[i - 1][j - 1][k + 1];
}
}
//结束进入s串
if(i > l && l) {
for(int k = 0; k <= i; ++k)
dp[i][l][k] += (k >= 1 ? dp[i - 1][l][k - 1] : 0) + dp[i - 1][l][k + 1];
}
for(int j = 0; j <= min(i, l); ++j) {
for(int k = 0; k <= i; ++k) {
dp[i][j][k] %= MOD;
printf("dp[%d][%d][%d]=%lld\n", i, j, k, dp[i][j][k]);
}
}
printf("\n");
}
printf("%lld\n", dp[2 * n][l][0] % MOD);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
这样会重复,比如
2
()
答案应该是2,但是上面这样会输出3。
解决的办法要利用字符串里面的KMP算法的next数组,得先放一下。