[Contest]2017 ACM/ICPC Asia Regional Urumqi Online(B D待补)
A. Banana
题意
给定$N$个猴子对香蕉喜欢的关系,和$M$个香蕉产自哪个地区的关系。输出所有猴子和地区的关系。
题解
暴力。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 59;
bool a[N][N], b[N][N], c[N][N];
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int main() {
int T = read();
while (T--) {
int n = read(), m = read();
memset(a, 0, sizeof(a));
for (int i = 1; i <= n; i++) {
int x = read(), y = read();
a[x][y] = 1;
}
memset(b, 0, sizeof(b));
for (int i = 1; i <= m; i++) {
int x = read(), y = read();
b[x][y] = 1;
}
memset(c, 0, sizeof(c));
for (int i = 1; i < N; i++) {
for (int j = 1; j < N; j++) {
for (int k = 1; k < N; k++) {
if (a[i][k] && b[k][j]) c[i][j] = 1;
}
}
}
for (int i = 1; i < N; i++) {
for (int j = 1; j < N; j++) {
if (c[i][j]) printf("%d %d\n", i, j);
}
}
printf("\n");
}
return 0;
}
B. Out-of-control cars
题意
题解
代码
C. Coconut
题意
一个人从$1$城依次到$N$城,从$i$城到$i+1$城需要$D_i$天,每天要喝$b$瓶饮料,到达$i$城后又可以获得$C_i$瓶饮料。判断这个人能否每天都喝饮料。
题解
模拟。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1009;
int c[N], d[N];
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int main() {
int t = read();
while (t--) {
int n = read(), b = read();
for (int i = 1; i <= n; i++) c[i] = read();
for (int i = 1; i < n; i++) d[i] = read();
ll cur = 0;
bool flag = true;
for (int i = 1; i < n; i++) {
cur += c[i];
cur -= d[i] * b;
if (cur < 0) {
flag = false;
break;
}
}
flag ? printf("Yes\n") : printf("No\n");
}
return 0;
}
D. Hack Portals
题意
题解
代码
E. Half-consecutive Numbers
题意
给定一个$N$,求最小的$r\geq N$使得$t_r=\frac{1}{2}r(r+1)$是一个平方数。
题解
考虑$\frac{1}{2}r(r+1)$是个完全平方数,要么$r=a2,r+1=2b2$,要么$r=2a2,r+1=b2$。
由于$n\leq10{16}$,故只需要枚举$108$以内的$a$即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll r[25] = {
0ll,
1ll,
8ll,
49ll,
288ll,
1681ll,
9800ll,
57121ll,
332928ll,
1940449ll,
11309768ll,
65918161ll,
384199200ll,
2239277041ll,
13051463048ll,
76069501249ll,
443365544448ll,
2584123765441ll,
15061377048200ll,
87784138523761ll,
511643454094368ll,
2982076586042449ll,
17380816062160328ll,
};
inline ll read() {
ll s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int main() {
/*
for (ll i = 1; i <= 1e8; i += 2) {
if (isSquare((i * i - 1) / 2)) printf("%lld\n", i * i - 1);
if (isSquare((i * i + 1) / 2)) printf("%lld\n", i * i);
}
*/
ll T = read();
for (ll t = 1; t <= T; t++) {
ll n = read(), i = 0;
for (; r[i] < n; i++);
printf("Case #%lld: %lld\n", t, r[i]);
}
}
F. Islands
题意
给定一个$N$个点$M$条边的有向图,求最少添加多少条边,使得新图变成强连通图。
题解
首先找出强连通分量,然后把每个强连通分量缩成一个点,这样就得到一个$DAG$。
然后设有$a$个节点的入度为$0$,$b$个节点的出度为$0$,则$res=max(a,b)$,因为只要把其中一边连成环即可。
注意特殊情况:当原图已经强连通时,答案是$0$而不是$1$!
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+9;
vector<int> g[N];
stack<int> s;
int in0[N], ou0[N];
int pre[N], low[N], scc[N], dfsClock, sccCnt;
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
inline void dfs(int u) {
pre[u] = low[u] = ++dfsClock;
s.push(u);
for (auto v : g[u]) {
if (!pre[v]) {
dfs(v);
low[u] = min(low[u], low[v]);
}
else
if (!scc[v]) low[u] = min(low[u], pre[v]);
}
if (low[u] == pre[u]) {
sccCnt++;
for (;;) {
int x = s.top(); s.pop();
scc[x] = sccCnt;
if (x == u) break;
}
}
}
inline void findScc(int n) {
dfsClock = sccCnt = 0;
memset(scc, 0, sizeof(scc));
memset(low, 0, sizeof(low));
memset(pre, 0, sizeof(pre));
for (int i = 1; i <= n; i++) if (!pre[i]) dfs(i);
}
int main() {
int T = read();
while (T--) {
int n = read(), m = read();
for (int i = 1; i <= n; i++) g[i].clear();
for (int i = 1; i <= m; i++) {
int u = read(), v = read();
g[u].push_back(v);
}
findScc(n);
for (int i = 1; i <= sccCnt; i++) in0[i] = ou0[i] = 1;
for (int u = 1; u <= n; u++) {
for (auto v : g[u]) {
if (scc[u] != scc[v]) in0[ scc[v] ] = ou0[ scc[u] ] = 0;
}
}
int a = 0, b = 0;
for (int i = 1; i <= sccCnt; i++) {
if (in0[i]) a++;
if (ou0[i]) b++;
}
int res = sccCnt == 1 ? 0 : max(a, b);
printf("%d\n", res);
}
return 0;
}
G. Query on a string
题意
给定两个字符串$S$和$T(|T|\leq 10)$,要求在$S$上维护:
- 操作$C\space i\space ch$:将$S$的第$i$个字符改成$ch$;
- 查询$Q\space i\space j$:返回$S$中第$i$到第$j$位$T$出现的次数。
题解
首先预处理出$S$中每一位开始连续$|T|$位能否和$T$匹配。
对于查询直接输出前缀和即可;
对于修改我们用树状数组维护预处理的结果,由于$|T|\leq 10$,修改一位最多影响十位的匹配结果,暴力修改即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+9;
char s[N], t[11];
int slen, tlen, bit[2 * N];
bool vis[N];
inline int lowbit(int x) {
return x & (-x);
}
inline void add(int x, int y) {
while (x < N) {
bit[x] += y;
x += lowbit(x);
}
}
inline int preSum(int x) {
int ret = 0;
while (x > 0) {
ret += bit[x];
x -= lowbit(x);
}
return ret;
}
inline int segSum(int l, int r) {
return preSum(r) - preSum(l - 1);
}
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int main() {
int T = read();
while (T--) {
int n = read();
scanf("%s", s + 1), slen = strlen(s + 1);
scanf("%s", t + 1), tlen = strlen(t + 1);
memset(bit, 0, sizeof(bit));
memset(vis, 0, sizeof(vis));
for (int i = 1; i + tlen - 1 <= slen; i++) {
bool flag = 1;
for (int j = 1; j <= tlen; j++) {
if (s[i + j - 1] != t[j]) {
flag = false;
break;
}
}
if (flag) add(i, 1), vis[i] = 1;
}
while (n--) {
char q[5]; scanf("%s", q);
if (q[0] == 'Q') {
int x = read(), y = read();
if (y - tlen + 1 < x) printf("0\n");
else printf("%d\n", segSum(x, y - tlen + 1));
}
if (q[0] == 'C') {
int p = read(); char c[5]; scanf("%s", c);
s[p] = c[0];
for (int i = max(1, p - tlen + 1); i <= min(slen - tlen + 1, p); i++) {
bool flag = 1;
for (int j = 1; j <= tlen; j++) {
if (s[i + j - 1] != t[j]) {
flag = 0;
break;
}
}
if (flag != vis[i]) {
if (vis[i]) add(i, -1);
else add(i, 1);
vis[i] = !vis[i];
}
}
}
}
printf("\n");
}
return 0;
}
H. Skiing
题意
求$DAG$上的最长路径。
题解
直接跑$SPFA$或者记忆化搜索即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+9;
const int INF = 0x3f3f3f3f;
int n, m, dist[N];
bool vis[N];
vector<pair<int, int> > g[N];
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
inline void SPFA() {
queue<int> q;
memset(vis, 0, sizeof(vis));
memset(dist, -INF, sizeof(dist));
q.push(0), vis[0] = 1, dist[0] = 0;
while (!q.empty()) {
int u = q.front();
q.pop(), vis[u] = 0;
for (auto e : g[u]) {
int v = e.first, w = e.second;
if (dist[u] + w > dist[v]) {
dist[v] = dist[u] + w;
if (!vis[v]) q.push(v), vis[v] = 1;
}
}
}
printf("%d\n", dist[n + 1]);
}
int main() {
int t = read();
while (t--) {
n = read(), m = read();
for (int i = 1; i <= n; i++) g[i].clear();
for (int i = 1; i <= m; i++) {
int s = read(), t = read(), l = read();
g[s].push_back( make_pair(t, l) );
}
for (int i = 1; i <= n; i++) {
g[0].push_back( make_pair(i, 0) );
g[i].push_back( make_pair(n + 1, 0) );
}
SPFA();
}
return 0;
}
I. Colored Graph
题意
求一种给$n$阶完全图的边$01$染色的方案,使得相同颜色的三元环最少,并求出这个最少数量。
题解
我们考虑任意一个含有不同颜色的三元环,一定是两个顶点的两条边颜色不同。
记$d_i$为与顶点$i$相连染$1$的边的数量,则相同颜色的三元环有$res=\binom{n}{3}-\frac{1}{2}\sum^{n}_{i=1}{d_i\times (n-1-d_i)}$个。
要让它最小,根据均值不等式我们容易得到:当$d_i=\frac{n-1}{2}$时取最小值$\binom{n}{3}-\frac{1}{2}\times n\times \frac{n-1}{2}\times (n-1-\frac{n-1}{2})$。
输出方案时,反正是要每个顶点连$\frac{n-1}{2}$条颜色为$1$的边,直接贪心地建图即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 509;
int g[N][N];
pii v[N];
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int main() {
int T = read();
while (T--) {
int n = read();
int res = n * (n - 1) * (n - 2) / 6 - n * ((n - 1) / 2 * (n - 1 - (n - 1) / 2)) / 2;
for (int i = 1; i <= n; i++) {
v[i].first = (n - 1) / 2;
v[i].second = i;
}
memset(g, 0, sizeof(g));
for (int i = 1; i <= n; i++) {
sort(v + i, v + n + 1);
for (int j = n; j > max(i, n - v[i].first); j--) {
g[ v[i].second ][ v[j].second ] = g[ v[j].second ][ v[i].second ] = 1;
v[j].first--;
}
}
printf("%d\n", res);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
printf("%d%c", i == j ? 0 : g[i][j] + 1, j == n ? '\n' : ' ');
}
}
}
return 0;
}
J. Our Journey of Dalian Ends
题意
给定一个无向图,不得重复经过同一顶点,求顶点$A$经过顶点$C$达到顶点$B$的最短路径。
题解
我们先把超级源连$A$和$B$,超级汇连$C$。
然后每个顶点拆成一个入点、一个出点,之间连一条容量为$1$、费用为$0$的边(注意:顶点$C$拆成的边容量为$2$)。
最后直接跑最小费用最大流。若到超级汇的流量不为$2$,则不存在这样的路径,否则输出最小费用即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e4;
const ll M = 2e5+9;
const ll INF = 0x3f3f3f3f;
const string s1 = "Dalian";
const string s2 = "Xian";
const string s3 = "Shanghai";
ll n, m;
map<string, ll> hsh;
inline ll read() {
ll s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
struct MCMF {
struct Edge {
ll fr, to, cap, flw, cst, nxt;
Edge() {}
Edge(ll fr, ll to, ll cap, ll flw, ll cst, ll nxt) {
this->fr = fr; this->to = to; this->cap = cap; this->flw = flw; this->cst = cst; this->nxt = nxt;
}
} e[M];
ll n, m, s, t, tot;
ll hed[M], dst[M], vis[M], pre[M], cag[M];
queue<ll> q;
void init() {
tot = 0;
for (ll i = s; i <= t; i++) hed[i] = -1;
}
void add(ll x, ll y, ll v, ll c) {
e[tot] = Edge(x, y, v, 0, c, hed[x]); hed[x] = tot++;
e[tot] = Edge(y, x, 0, 0, -c, hed[y]); hed[y] = tot++;
}
bool SPFA(ll& flow, ll& cost) {
for (ll i = s; i <= t; i++) dst[i] = INF, vis[i] = 0;
while (!q.empty()) q.pop();
q.push(s); dst[s] = 0; vis[s] = 1; pre[s] = 0; cag[s] = INF;
while (!q.empty()) {
ll u = q.front(); q.pop(); vis[u] = 0;
for (ll i = hed[u]; ~i; i = e[i].nxt) {
Edge& edge = e[i];
if (edge.cap > edge.flw && dst[edge.to] > dst[u] + edge.cst) {
dst[edge.to] = dst[u] + edge.cst;
pre[edge.to] = i;
cag[edge.to] = min(cag[u], edge.cap - edge.flw);
if (!vis[edge.to]) q.push(edge.to), vis[edge.to] = 1;
}
}
}
if (dst[t] == INF) return 0;
flow += cag[t];
cost += dst[t] * cag[t];
ll u = t;
while (u != s) {
e[ pre[u] ].flw += cag[t];
e[ pre[u] ^ 1 ].flw -= cag[t];
u = e[ pre[u] ].fr;
}
return 1;
}
ll minCost() {
ll flow = 0, cost = 0;
while (SPFA(flow, cost));
return flow != 2 ? -1 : cost;
}
} mcmf;
int main() {
ll T = read();
while (T--) {
n = 0, m = read(); hsh.clear();
mcmf.n = 2 * N + 2; mcmf.s = 0; mcmf.t = 2 * N + 1; mcmf.init();
for (ll i = 1; i <= m; i++) {
string u, v; cin >> u >> v;
ll c = read();
if (hsh[u] == 0) {
hsh[u] = ++n;
if (u != s3) mcmf.add(hsh[u], hsh[u] + N, 1, 0);
}
if (hsh[v] == 0) {
hsh[v] = ++n;
if (v != s3) mcmf.add(hsh[v], hsh[v] + N, 1, 0);
}
if (u == s3) mcmf.add(hsh[u], hsh[v], 1, c);
else mcmf.add(hsh[u] + N, hsh[v], 1, c);
if (v == s3) mcmf.add(hsh[v], hsh[u], 1, c);
else mcmf.add(hsh[v] + N, hsh[u], 1, c);
}
mcmf.add(mcmf.s, hsh[s3], 2, 0);
mcmf.add(hsh[s2] + N, mcmf.t, 1, 0);
mcmf.add(hsh[s1] + N, mcmf.t, 1, 0);
printf("%lld\n", mcmf.minCost());
}
return 0;
}