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*/

C.Museums Tour

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

$n,m \leq 100000,d \leq 50$

sol:

卡空间神题,卡掉了 w \times 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;
}

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;
}

原文地址:https://www.cnblogs.com/Kong-Ruo/p/10499605.html

时间: 2024-08-02 09:26:26

Codeforces Round #545 (div 1.)的相关文章

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

每次CF后,我的rating下降,掉分让我悲痛欲绝 ——题记 ### 前言 这次 CF 时间可谓是极好的,17:05 开始,时长 2h 30min. 4:40 回到家,开电脑. 在看了一会书后,比赛开始了. ### 正文 首先,第一个掉分的 flag 出现了! 卡,卡,卡! CF 又双叒叕卡了! 1分钟过去了…… 2分钟过去了…… 3分钟过去了…… Dashboard 一直打不开.. 当时间已经接近 17:10 分的时候,我终于打开了 A 题.. 然而,这还并不是我掉分的所有原因. 先迅速浏览了

Codeforces Round #545 (Div. 2) D

链接:http://codeforces.com/contest/1138/problem/D 啊啊啊啊啊啊,自闭啊,比赛的时候判断条件 if(s1[i-1]=='0') aa++;写成了 if(s1[i]=='0') aa++;少写了个-1,被hack了,上分场变成了掉分场. 思路; 题目需要t字符串出现次数最多,那么找到最大的重叠部分就好了,然后依次加上就好了 主要就是要找到字符串t与本身的重叠部分,,假设有两个t,第一个t不变,第二个t向右移动: 比如: 10101010 -1010101

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

全世界最笨的人 出现了 这题真的好欺骗我的感情,表面上先给我过了,让我快乐一下,背地里再把我给踢掉 TUT干嘛这么坏啊!!!!!!!!!!!!! 好吧,其实这个错误昨天晚上就反过了 但是世界上最笨的女同学她是不会改的 如果你下次还不改 那你还是世界上最笨的女同学 如果你改了 那你...... TUT... 我们来分析一下,为什么这个女同学,连A题都过不了 这题的题意很简单,就是要求长得像AAABBB的序列的最大值 我先选了一种很弱智的方法 就是,先求前面那个连续序列的长度,再求后面那个连续序列的

Codeforces Round #545 Div. 1自闭记

A:求出该行该列各有多少个比其小的取max,该行该列各有多少个比其大的取max,加起来即可. #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 1010 char ge

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

#include<bits/stdc++.h>using namespace std;const int N=1000007;char s1[N],s2[N];int len1,len2;int nex[N];int cnt1[7],cnt2[7];int main(){    scanf("%s %s",s1+1,s2+1);    len1=strlen(s1+1);    len2=strlen(s2+1);    for(int i=1;i<=len1;i++

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿

Codeforces Round #424 (Div. 2) C. Jury Marks(乱搞)

题目链接:Codeforces Round #424 (Div. 2) C. Jury Marks 题意: 给你一个有n个数序列,现在让你确定一个x,使得x通过挨着加这个序列的每一个数能出现所有给出的k个数. 问合法的x有多少个.题目保证这k个数完全不同. 题解: 显然,要将这n个数求一下前缀和,并且排一下序,这样,能出现的数就可以表示为x+a,x+b,x+c了. 这里 x+a,x+b,x+c是递增的.这里我把这个序列叫做A序列 然后对于给出的k个数,我们也排一下序,这里我把它叫做B序列,如果我

[Codeforces] Round #352 (Div. 2)

人生不止眼前的狗血,还有远方的狗带 A题B题一如既往的丝帛题 A题题意:询问按照12345678910111213...的顺序排列下去第n(n<=10^3)个数是多少 题解:打表,输出 1 #include<bits/stdc++.h> 2 using namespace std; 3 int dig[10],A[1005]; 4 int main(){ 5 int aa=0; 6 for(int i=1;;i++){ 7 int x=i,dd=0; 8 while(x)dig[++dd