本场链接:AtCoder Beginner Contest 218
A - Weather Forecast
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
int main()
{
int n;string s;cin >> n >> s;
if(s[n - 1] == 'o') cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
B - qwerty
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
int main()
{
forn(i,1,26)
{
int x;cin >> x;
cout << char(x + 'a' - 1);
}
cout << endl;
return 0;
}
C - Shapes
首先考虑实现逆时针旋转整个字符数组:不难观察到相当于把每一列换到行上,并且第一列换到最后一行,第二列换到倒数第二行...如此可以将四种形态直接求出来,剩下的问题为:是否存在一种平移方式使得:两个图形重叠。可以以一定的顺序遍历所有黑点,如果存在一种平移的方式使得两个图形重叠,那么所有点相对偏移的坐标全部相同,抠出所有点进行check即可。
注意形态一共有四种,可以不旋转,并且两个图形内的点数的个数不一定是相同的。甚至可以没有。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
const int N = 205;
char s[N][N],t[N][N],r[N][N];
bool solve(int n)
{
vector<pii> target; forn(i,1,n) forn(j,1,n) if(t[i][j] == '#') target.push_back({i,j});
vector<pii> cur; forn(i,1,n) forn(j,1,n) if(s[i][j] == '#') cur.push_back({i,j});
if(target.size() != cur.size()) return 0;
if(target.empty()) return 1;
int dx = target[0].x - cur[0].x,dy = target[0].y - cur[0].y;
forn(i,0,target.size() - 1) if(target[i].x - cur[i].x != dx || target[i].y - cur[i].y != dy) return 0;
return 1;
}
int main()
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%s",s[i] + 1);
forn(i,1,n) scanf("%s",t[i] + 1);
bool ok = 0;
forn(_,1,4)
{
forn(j,1,n) forn(i,1,n) r[n - j + 1][i] = s[i][j];
forn(i,1,n) forn(j,1,n) s[i][j] = r[i][j];
ok |= solve(n);
}
if(ok) puts("Yes");
else puts("No");
return 0;
}
D - Rectangles
显然考虑枚举两个点 ((i,j)),并且首先保证两个点满足:(y_i == y_j) 这样相当于枚举一条竖线。同时可以保证 (x_i < x_j) 这样 (i) 点一定在 (j) 点的上方,现在剩下的问题相当于求:有多少个二元组((z,k))满足这四个点可以形成一个矩形,那么首先前面的两个点 ((z,k)) 显然也必须要是一条竖线,并且保证 (x_z < x_k) 的前提下需要有: (x_z == x_i) 且 (x_k == x_j)。如此可以直接用 map 维护二元组的数量,统计答案即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
const int N = 2005;
pii p[N];
int main()
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d%d",&p[i].x,&p[i].y);
map<pii,int> st;
ll res = 0;
forn(i,1,n) forn(j,1,n)
{
int x1 = p[i].x,x2 = p[j].x,y1 = p[i].y,y2 = p[j].y;
if(y1 != y2 || x1 > x2 || i == j) continue;
res += st[{x1,x2}];
++st[{x1,x2}];
}
printf("%lld
",res);
return 0;
}
E - Destruction
删除一条边显然比较无理,考虑把问题逆向成建边,相当于一开始就把所有的边删掉,增加最少的边使得答案减少的最少,即答案的最大值。因为在这样反过来的问题上,正权边相当于减少权值,负权边相当于增加权值,所以所有负权边全部加入,并且正权边尽可能少,这样的问题答案等价于求 MST。直接套版子即可 (res = sum - MST)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
const int N = 2e5+7,M = 2 * N;
struct Edge
{
int u,v,w;
bool operator<(const Edge& rhs) const
{
return w < rhs.w;
}
}edges[M];
int fa[N];
int n,m;
int find(int x)
{
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
ll kruskal()
{
sort(edges + 1,edges + m + 1);
ll res = 0;
forn(i,1,m)
{
int u = edges[i].u,v = edges[i].v,w = edges[i].w;
u = find(u),v = find(v);
if(u == v)
{
if(w < 0) res += w;
continue;
}
fa[u] = v;
res += w;
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
forn(i,1,n) fa[i] = i;
ll sum = 0;
forn(i,1,m)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
edges[i] = {u,v,w};
sum += w;
}
printf("%lld
",sum - kruskal());
return 0;
}
F - Blocked Roads
因为所有边权为 (1),所以单次求解 SSSP 的复杂度是 (O(n + m))的,由于 (m = n^2)故单次求解为 (O(n^2))。那么直接暴力枚举删除所有边再拓展 BFS 求最短路的复杂度就是 (O(n^4))。
考虑首先求出一条最短路,不难想到:如果删去的边不在这条最短路上,那么删去他不会对最短路产生任何影响,所以答案不变。如果边就在最短路上,则在删除他的前提下直接 BFS,由于 (1 - n) 的最短路上至多有 (n-1) 条边,所以最多会额外跑 (n - 1) 次 BFS,这样产生的额外代价是 (O(n^3)) 的。整体的复杂度即 (O(n^3))。可以通过本题。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
const int N = 405,M = N * N;
int edge[M],succ[M],ver[N],ID[M],idx;
int dist[N];
int n,m;
pii from[N];
bool st[M];
void add(int u,int v,int id)
{
edge[idx] = v;
ID[idx] = id;
succ[idx] = ver[u];
ver[u] = idx++;
}
int bfs(int del = 0)
{
memset(dist,0x3f,sizeof dist);dist[1] = 0;
queue<int> q;q.push(1);
while(!q.empty())
{
int u = q.front();q.pop();
for(int i = ver[u];~i;i = succ[i])
{
if(ID[i] == del) continue;
int v = edge[i];
if(dist[v] > dist[u] + 1)
{
dist[v] = dist[u] + 1;
from[v] = {u,ID[i]};
q.push(v);
}
}
}
return dist[n] == 0x3f3f3f3f ? -1 : dist[n];
}
int main()
{
memset(ver,-1,sizeof ver);
scanf("%d%d",&n,&m);
forn(i,1,m)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v,i);
}
int res = bfs(),cur = n;
if(res == -1)
{
forn(i,1,m) puts("-1");
return 0;
}
while(cur != 1)
{
int u = from[cur].x,id = from[cur].y;
st[id] = 1;
cur = u;
}
forn(i,1,m)
{
if(!st[i]) printf("%d
",res);
else printf("%d
",bfs(i));
}
return 0;
}