Codeforces Round #621 (Div. 1 + Div. 2)
题目链接:https://codeforces.com/contest/1307
A - Cow and Haybales
一开始读错了题,看见t神0分钟过了才知道确实是签到。
题意:给一个非负整数序列,每次操作可以把一个非零数字-1,相邻的数字+1。求至多能使得第1个数字变得多大?
题解:贪心,尽量从最近的数字往第1个数字搬。事实上因为数据量极小,每次搬1确实也可以。
B - Cow and Friend
题意:给一个非负整数序列,每次操作先选一个数字,然后在二维平面上走恰好这个数字长度,求最少要多少次操作才能从(0,0)到(x,0)。
题解:首先假如有个与x相等的数字,那么答案就是1。其次,假如最大的数字maxa>=x/2,那么可以走一个等腰三角形到达(在完全伸开到达2*max和完全折叠到达0之间必有一点恰好等于x),答案是2。那么得到一个理所当然的贪心,就是先往x走到尽可能近,然后假如某一步走到超过了x,就把最后的两步折起来变成一个等腰三角形。
C - Cow and Message
题意:给一个字符串,求其中出现次数最多的子序列的出现次数。
题解:当时猜了一个结论,就是一定是两个不同的字母的组合成的长度为2的序列,由于这样的组合本身就26*25种,所以可以直接莽,假如枚举的是"ab",那么记录"a"的数量,然后每次遇到"b"就把这个前面的"a"的数量加上去。然后相了一想,假如是"aaaaa",答案对应的序列就应该是"aa",然后在cnta*(cnta+1)/2和cnta*(cnta-1)/2之间纠结,最后从"aaa"中确定是后者。准备提交的时候想到,假如只有1个字符,那我的答案岂不是0?还好立刻补了一发。其实假如把初始值设为1就没有这个问题了。最后发现其实两个相同的字母的统计方法也可以包含在第1种情况中。
感觉应该会有人因为只有1个字母的数据挂掉。确实,main test的36就是这个东西,有几个紫名爷掉了。
当时hack了一个用int的人,没想到被
#define int long long
制裁了。
char s[100005];
int cnt[128];
void test_case() {
scanf("%s", s + 1);
int n = strlen(s + 1);
for(int i = 1; i <= n; ++i)
++cnt[s[i]];
ll ans = 0;
for(int i = 'a'; i <= 'z'; ++i)
ans = max(ans, 1ll * cnt[i]);
for(int i = 'a'; i <= 'z'; ++i)
ans = max(ans, 1ll * cnt[i] * (cnt[i] - 1ll) / 2ll);
for(int i = 'a'; i <= 'z'; ++i) {
for(int j = 'a'; j <= 'z'; ++j) {
if(j == i)
continue;
if(1ll * cnt[i]*cnt[j] <= ans)
continue;
int cnti = 0;
ll sum = 0;
for(int k = 1; k <= n; ++k) {
if(s[k] == i)
++cnti;
else if(s[k] == j)
sum += cnti;
}
ans = max(ans, sum);
}
}
printf("%lld\n", ans);
}
*D - Cow and Fields
第一次在比赛过程中能过标签带graph的1900(猜)的题耶!
题意:给n个点,m条边的无向连通图,每天要从1号点走到n号点。给出k个特殊点,要求在这些特殊点之间加入恰好1条边,使得整个图的从1到n的最短路尽可能长,求出这个最短路的长度。
题解:首先有一个很显然但是没什么卵用的观察,就是假如特殊点之间有边,那么加一条重边就是答案。这种题好像做过几次有点套路了?还是说变强了?当时猜测是要按dis1[x]的大小分层考虑,每一层的x都向同层或者深层的y连边,这样的最短路就保证为dis1[x]+1+disn[y],但是不知道怎么证明。今天补上这个证明。
设这两个特殊点为x,y,考虑必须走新加的边的最短路,那么这个最短路的长度必定为Lxy=min(dis1[x]+1+disn[y],dis1[y]+1+disn[x]),然后从所有的x,y的组合中选出最大的Lxy,与原本的最短路取min,就是题目要找的答案。麻烦的地方在于一开始的这个min,毕竟这条边是有两种走法的,然后隐隐约约感觉到,貌似可以通过额外的信息确定这条边要按哪个方向走(才可能形成最短路)。
虽然要去掉这个min要分很多种情况,但是不失一般性,可以先规定dis1[x]<=dis1[y],然后分disn[x]<disn[y],disn[x]=disn[y],disn[x]>disn[y]三种情况讨论。
先考虑最简单的disn[x]=disn[y],此时直接提取到外面Lxy=disn[y]+1min(dis1[x],dis1[y])=dis1[x]+1+disn[y]。要取这个的最大值,只需要按dis1[x]排序满足dis1[x]<=dis1[y]后,使用一个后缀最大值即可。
可能存在某些点,满足dis1[x]<=dis1[y],同时disn[x]<disn[y],例如一个T形的,1号点和n号点在两端,x在中间的交叉处,y在下面。这个时候的最短路不可能比dis1[x]+disn[x]=dis1[n]更短,直接忽略,所以把不优的答案dis1[x]+1+disn[y]也合并进来并不会产生不良影响。
可能存在某些点,满足dis1[x]<=dis1[y],同时disn[x]>disn[y],比如x和y都在原本的从1号点到n号点的最短路上,就是满足这样的式子,这种情况最小值确实就是dis1[x]+1+disn[y],因为它(有可能)缩短了最短路,而且从dis1[x]+1+disn[y]<dis1[y]+1+disn[x]也可以知道直接合并dis1[x]+1+disn[y]即可。
综上所述,按dis1[x]排序,然后取disn[y]的后缀最大值。
vector<int> G[200005];
int dis1[200005];
int disn[200005];
bool vis[200005];
int *dis;
priority_queue<pair<int, int> > pq;
void dijkstra(int s, int n) {
while(!pq.empty())
pq.pop();
for(int i = 1; i <= n; ++i) {
dis[i] = 0x3f3f3f3f;
vis[i] = 0;
}
dis[s] = 0;
pq.push({-dis[s], s});
while(!pq.empty()) {
int u = pq.top().second;
pq.pop();
if(vis[u])
continue;
vis[u] = 1;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(!vis[v] && dis[v] > dis[u] + 1) {
dis[v] = dis[u] + 1;
pq.push({-dis[v], v});
}
}
}
}
int a[200005];
pii PII[200005];
int sufmax[200005];
void test_case() {
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= k; ++i)
scanf("%d", &a[i]);
while(m--) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dis = dis1;
dijkstra(1, n);
dis = disn;
dijkstra(n, n);
for(int i = 1; i <= k; ++i)
PII[i] = {dis1[a[i]], a[i]};
sort(PII + 1, PII + 1 + k);
sufmax[k] = disn[PII[k].second];
for(int i = k - 1; i >= 1; --i)
sufmax[i] = max(sufmax[i + 1], disn[PII[i].second]);
/*for(int i = 1; i <= k; ++i)
printf("%d %d\n", PII[i].first, PII[i].second);
for(int i = 1; i <= k; ++i)
printf("%d\n", sufmax[i]);*/
int ans = disn[1], maxans = 0;
for(int i = 1; i < k; ++i) {
int u = PII[i].second;
int tmp = dis1[PII[i].second] + 1 + sufmax[i + 1];
if(tmp >= ans) {
printf("%d\n", ans);
return;
}
maxans = max(maxans, tmp);
}
printf("%d\n", maxans);
}
反思:最短路其实可以用bfs的,结合桶排序或者其他什么DAG上dp可以把复杂度确实降低到线性。
收获:貌似最短路的变化一般都从最短路径生成树(以及dis[v]=dis[u]+w的点也是一条最短路),以及根据距离分层的算法来考虑?