第7分钟过A,第48分钟过B,然后憋到结束也没出C.
惨啊.
A
这是我在CF上见到的实现起来最简单的题目了.
会发现,想要达成"任意一个格子都有逃离的路线"意味着不能存在某一区域被墙圈住,如果把墙看作边,把格子的顶点看作节点,那么就转化为最终的(无向)图中不能存在环.
给定了一定数量的节点,要求在其中添加尽可能多的边,并且不能存在环.这显然会形成树,边数即为节点数-1.
之后只需要根据a,b计算出原有墙数x,节点数y,输出x-(y-1)即可.发现计算结果即为a*b.
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int a, b;
void solve(){
scanf("%d%d", &a, &b);
printf("%d
", a * b);
}
int main(){
int t;
scanf("%d", &t);
while(t--) solve();
return 0;
}
B
先推导一波观察一下,a[1]=s%m,a[2]=(a[1]+c)%=(s%m+c)%m=(s+c)%m,可知a[i]=(s+(i-1)c)%m.
会看出来,对于任意一个存在解的数组,a[i]-a[i-1]的值域只有两个或者一个值.其中,只有一个值的情况对应的数组为等差数列,这是m显然可取正无穷.
而对于值域有两个值的情况下:
1 9 17 6 14 3
a[i]-a[i-1]:8 8 -11 8 -11
发现解即为m=8-(-11)=19,c=8.这个结论具有普遍性.
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n, a[100010];
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", a + i);
if (n <= 2) {
puts("0");
return;
}
bool b1 = false, b2 = false;
int v1, v2;
int big = 0;
for (int i = 1; i <= n; i++) {
big = max(big, a[i]);
if (i == 1) continue;
if (!b1) {
b1 = true, v1 = a[i] - a[i - 1];
} else if (!b2) {
b2 = true, v2 = a[i] - a[i - 1];
} else if (a[i] - a[i - 1] != v1 && a[i] - a[i - 1] != v2) {
if (v1 == v2)
v2 = a[i] - a[i - 1];
else {
puts("-1");
// printf("!!!%d %d
", v1, v2);
return;
}
}
}
if (v1 > v2) swap(v1, v2);
if (v1 == v2)
puts("0");
else if (v2 - v1 > big)
printf("%d %d
", v2 - v1, v2);
else
puts("-1");
}
int main() {
int t;
scanf("%d", &t);
while (t--) solve();
return 0;
}
实际上这显然是一个适合使用unordered_set的场景,参考这篇补题.
C
思维力不够,赛时没做出来.一直没有注意到m/2含有的特殊意义.
一旦有某人出现了m/2次后,剩余的人就不可能出现m/2次了.这是突破口.
并且显然地,如果发现某人可选的天数不超过m/2,可以直接让他出席其中所有闲置的天.
得到如下流程:
依次检查每个人的出现次数是否超过m/2
否-使其出席所有可出席天
是-检查有多少天只有他一人可出席
不超过m/2-先出席这些天,再出席其它天直至此人共计出席了m/2天,且此后处理任何人时只需使其出席其所有闲置可选天
超过m/2-本组数据无解
最后需要检查一下是否所有天都安排上了.
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
vector<int> d[100010]; // who appeared on day i
vector<int> p[100010]; // people i appeared on which day
int n, m, ct[100010]; // appear time of people
int ans[100010];
void solve() {
scanf("%d%d", &n, &m);
memset(ans, 0, sizeof(ans));
memset(ct, 0, sizeof(ct));
for (int i = 1; i <= n; i++) p[i].clear();
for (int i = 1; i <= m; i++) {
d[i].clear();
int k, x;
scanf("%d", &k);
while (k--) {
scanf("%d", &x);
d[i].push_back(x);
p[x].push_back(i);
ct[x]++;
}
}
int bd = (m - 1) / 2 + 1;
bool good = false;
for (int i = 1; i <= n; i++) {
if (good) {
for (auto j : p[i])
if (!ans[j]) ans[j] = i;
} else if (ct[i] <= bd)
for (auto j : p[i]) ans[j] = i;
else {
good = true;
int c = 0;
for (int j = 1; j <= m; j++)
if (!ans[j] && d[j].size() == 1 && d[j][0] == i) {
ans[j] = i;
c++;
}
if (c > bd) {
puts("NO");
return;
}
for (auto j : p[i]) {
if (c == bd) break;
if (!ans[j]) {
ans[j] = i;
c++;
}
}
}
}
for (int i = 1; i <= m; i++)
if (!ans[i]) {
puts("NO");
return;
}
puts("YES");
for (int i = 1; i <= m; i++) printf("%d ", ans[i]);
puts("");
}
int main() {
int t;
scanf("%d", &t);
while (t--) solve();
return 0;
}
总结就是注意系数,并且敢于假设前提,往后多想几步.
不能一厢情愿地认为是某种算法,这可能是一道构造题.