Codeforces Round #545 (div 1.)

Posted kong-ruo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #545 (div 1.)相关的知识,希望对你有一定的参考价值。

B.Camp Schedule

给两个 $01$ 串 $s$ 和 $t$,你可以将 $s$ 串任意重排,要求最大化 $t$ 在 $s$ 子串中出现的次数,可以重叠

$|s|,|t| leq 500000$

sol:

看到可以重叠,考虑最大化利用重叠部分

重叠部分是这次 $t$ 串的结束和下次 $t$ 串的开始,也就是 $t$ 串的一个 $border$

先放一个 $border$ ,之后一直放 $t$ 串的除 $border$ 以外的部分就可以了

技术图片
#include <bits/stdc++.h>
#define LL long long
using namespace std;
#define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar())if (ch == -)f = -f;
    for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - 0;
    return x * f;
}
char s[500010], t[500010], border[500010], ans[500010], pborder[500010];
int fail[500010], cnt[2], pcnt[2], ncnt[2];
int chk() {return (cnt[1] >= pcnt[1]) && (cnt[0] >= pcnt[0]);}
int chk2() {return (cnt[1] >= ncnt[1]) && (cnt[0] >= ncnt[0]);}
int main() {
    scanf("%s", s + 1); scanf("%s", t + 1);
    int n = strlen(s + 1), m = strlen(t + 1);
    rep(i, 1, n) cnt[s[i] - 0]++;
    int j = 0; for(int i=2;i<=m;i++) {
        while(j && t[j + 1] != t[i]) j = fail[j];
        if(t[i] == t[j + 1]) j++;
        fail[i] = j;
    } int blen = fail[m];
    rep(i, 1, blen) border[i] = t[i], pcnt[t[i] - 0]++;
    //cout << border+1 << endl;
    rep(i, blen+1, m) pborder[i - blen] = t[i], ncnt[t[i] - 0]++;
    //cout << pborder+1 << endl;
    int clen = strlen(pborder + 1);
    int dfn = 1;
    if(chk()) {
        cnt[1] -= pcnt[1]; cnt[0] -= pcnt[0];
        rep(i, 1, blen) ans[i] = border[i]; dfn = blen;
        while(chk2()) {
            rep(i, 1, clen)
                ans[dfn + i] = pborder[i];
            dfn += clen;
            cnt[1] -= ncnt[1]; cnt[0] -= ncnt[0];
        }
        while(cnt[1]) {
            ans[++dfn] = 1;
            cnt[1]--;
        }
        while(cnt[0]) {
            ans[++dfn] = 0;
            cnt[0]--;
        }
        cout << (ans + 1) << endl;
    }
    else {
        cout << (s+1) << endl;
    }
}
/*1111111001100
101*/
View Code

 

C.Museums Tour

一个有向图,每个点有一个博物馆,每个博物馆有一个长度为 $d$ 的开放时间表(是循环的),你可以进行一次任意天的旅行,但是每天必须走一步,问最多能参观多少博物馆

$n,m leq 100000,d leq 50$

sol:

卡空间神题,卡掉了 w imes h

虽然他还是 rank 1.

有很多基于强连通分量的做法,这里写一种比较稳的

把每个点拆成 $d$ 个点,求一个强连通分量

在强连通分量里算一下有哪些点可以走到即可,显然只需要 $(x,y)$ 这个点在强连通分量里且 $x$ 在第 $y$ 天开放就可以通过一直绕圈圈走到

外面是个 DAG,做个 dp 即可

技术图片
#include <bits/stdc++.h>
#define LL long long
using namespace std;
#define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar())if (ch == -)f = -f;
    for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - 0;
    return x * f;
}
const int maxn = 5000010;
int n, m, d;
int first[maxn], to[maxn], nx[maxn], cnt;
inline void add(int u, int v) {
    to[++cnt] = v;
    nx[cnt] = first[u];
    first[u] = cnt;
}
int dfn[maxn], low[maxn], st[maxn], top, bl[maxn], scc, _tim;
char grid[100010][60];
int now;
vector<int> G2[maxn], ccs[maxn];
inline void Tarjan(int x) {
    dfn[x] = low[x] = ++_tim; st[++top] = x;
    for(int i=first[x];i;i=nx[i]) {
        if(!dfn[to[i]]) {
            Tarjan(to[i]);
            low[x] = min(low[x], low[to[i]]);
        }
        else if(bl[to[i]] == -1) low[x] = min(low[x], dfn[to[i]]);
    }
    if(low[x] == dfn[x]) {
        now = -1;
        do {
            now = st[top--];
            bl[now] = scc;
            ccs[scc].push_back(now);
        }while(now != x);
        scc++;
    }
}
int dp[maxn], hsh[maxn];
int main() {
    n = read(), m = read(), d = read();
    memset(bl, -1, sizeof(bl));
    rep(i, 0, m-1) {
        int u = read() - 1, v = read() - 1;
        rep(j, 0, d-1) add(u * d + j, v * d + (j + 1) % d);
    }
    rep(i, 0, n-1) scanf("%s", grid[i]);
    rep(i, 0, n-1) rep(j, 0, d-1) grid[i][j] -= 0;
    rep(i, 0, n*d-1) if(!dfn[i]) Tarjan(i);
    rep(i, 0, scc-1) {
        sort(ccs[i].begin(), ccs[i].end());
        int last = -1;
    //    cout << i << ":" << endl;
    //    for(auto j : ccs[i]) cout << j << " ";
    //    cout << endl;
        for(auto j : ccs[i]) {
            if(j / d != last) { 
                if(grid[j / d][j % d]) {
                    //cout << last << ":" << j << " " << d << endl;
                    hsh[i]++;
                    last = j / d;
                }
            }
        }
    }
    rep(x, 0, n*d-1)
        for(int i=first[x];i;i=nx[i])
            if(bl[x] != bl[to[i]]) 
                G2[bl[x]].push_back(bl[to[i]]);
                //cout << bl[x] << "->" << bl[to[i]] << endl;
                //ind[bl[to[i]]]++;
    //rep(i, 0, scc-1) cout << hsh[i] << " ";
    rep(i, 0, scc-1) {
        dp[i] = hsh[i];
        for(auto h : G2[i]) dp[i] = max(dp[i], dp[h] + hsh[i]);
    }
    cout << dp[bl[0]] << endl;
}
View Code

 

D.Cooperative Game

交互题

有一个链表,已知它有闭环,你一开始有 $10$ 个指针,都指向表头

每次操作你可以让若干个指针向前移动一个单位,交互器会返回现在哪些指针在同一个单位

你现在要让所有指针都正好在环的起始节点,并在都在环的起始节点时输出 $done$

操作次数限制为 $3n$ 次,你不知道环长,不知道链长,甚至不知道 $n$

$n leq 1000$

sol:

经典题...网上都是解法但我没搜

经典解法是这样的:

1.设置一个“快节点”,一个“慢节点”,快节点每次走两步,慢节点每次走一步

(具体到这道题就是第一步让他俩一起走,第二步只有快节点走)

2.这两个点如果相遇,说明链表有环

(这题一定有)

3.再把两个节点一个放回表头,另外一个在相遇点不动,把这两个点一起动,相遇的地方就是环的起始节点

(这题你相遇点有两个点,然后你惊喜的发现起始节点还有 8 个点没动,那就大家一起走就可以了)

4.输出 done,Pretest Passed,Accepted,然后大骂出题人

考验大家使用 Google / wiki 能力?

Stop Writing Problems

技术图片
#include<bits/stdc++.h>
#define LL long long
#define rep(i,s,t) for(register int i = (s),i##end = (t); i <= i##end; ++i)
#define dwn(i,s,t) for(register int i = (s),i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
    int x=0,f=1;char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())if(ch==-)f=-f;
    for(;isdigit(ch);ch=getchar())x=10*x+ch-0;
    return x*f;
}
const int maxn = 100010;
string ch;
int getres() {
    int x; cin >> x;
    rep(i, 1, x) cin >> ch;
    return x;
}
int main() {
    do {
        cout << "next 0 1" << endl; getres();
        cout << "next 0" << endl;
    }while(getres() > 2);
    do {
        cout << "next 0 1 2 3 4 5 6 7 8 9" << endl;
    }while(getres() > 1);
    cout << "done" << endl;
}
View Code

 

以上是关于Codeforces Round #545 (div 1.)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #545 Div. 1自闭记

Codeforces Round #545 (Div. 2) 掉分记

Codeforces Round #545 (Div. 2)D(KMP,最长公共前后缀,贪心)

Codeforces Round #545 (Div. 2)A. Sushi for Two

「日常训练」Woodcutters(Codeforces Round 303 Div.2 C)

Codeforces Round #436 E. Fire(背包dp+输出路径)