AtCoder Regular Contest 80

链接

C. 4-adjacent

给定序列$a_i$,询问是否存在一个排列,满足$a_{p[i]}* a_{p[i + 1]}$是4的倍数

贪心构造

首先把只是2的倍数的数拿出来,放在最右边

前面把是1的倍数的数和是4的倍数的数交替放置即可

之后随意判断即可

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

extern inline char gc() {
    static char RR[23456], *S = RR + 23333, *T = RR + 23333;
    if(S == T) fread(RR, 1, 23333, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;
}

#define ri register int
int n, n2, n4, flag;

int main() {
    n = read();
    for(ri i = 1; i <= n; i ++) {
        int x = read();
        if(x % 4 == 0) n4 ++;
        else if(x % 2 == 0) n2 ++;
    }
    n -= n2;
    if(n & 1) {
        if(n2) flag = (n4 > n / 2);
        else flag = (n4 >= n / 2);
    }
    else flag = (n4 >= n / 2);
    if(flag) printf("Yes
");
    else printf("No
");
    return 0;
}

D.Grid Coloring

对$R*W$的格子进行染色,需要使颜色$i$的出现次数为$c_i$

且同一颜色形成联通块,询问是否可行并给出一组方案

S形涂色即可

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define ri register int
#define sid 105

int H, W, n, id;
int col[sid][sid], ord[sid * sid];

int main() {
    scanf("%d%d%d", &H, &W, &n);
    for(ri i = 1; i <= n; i ++) {
        int x; scanf("%d", &x);
        while(x --) ord[++ id] = i;
    }
    id = 0;
    for(ri i = 1; i <= H; i ++)
    for(ri j = 1; j <= W; j ++)
    col[i][j] = ord[++ id];
    for(ri i = 2; i <= H; i += 2)
    reverse(col[i] + 1, col[i] + W + 1);
    for(ri i = 1; i <= H; i ++, printf("
"))
    for(ri j = 1; j <= W; j ++)
    printf("%d ", col[i][j]);
    return 0;
}

E.Young Maids

题意略复杂,直接看原题面吧...

从前往后去贪心

每次选择的一定是某个区间中的奇(偶)最小值后其后的偶(奇)最小值

注意奇偶的判断即可

复杂度$O(n log n)$

一开始表示平衡树裸题,然后点开题解,还是打st表吧

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

extern inline char gc() {
    static char RR[23456], *S = RR + 23333, *T = RR + 23333;
    if(S == T) fread(RR, 1, 23333, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;
}

#define sid 200050
#define inf 1e9
#define ri register int

int n;
int bit[sid], l_g[sid];
int st[sid][21][2], v[sid][2], arc[sid];

void Init() {
    for(ri i = 0; i <= 30; i ++) bit[i] = 1 << i;
    for(ri i = 2; i <= n; i ++) l_g[i] = l_g[i >> 1] + 1;
    for(ri o = 0; o <= 1; o ++)
    for(ri i = 1; i <= n; i ++) st[i][0][o] = v[i][o];
    for(ri o = 0; o <= 1; o ++)
    for(ri j = 1; bit[j] <= n; j ++)
    for(ri i = 1; i + bit[j] - 1 <= n; i ++)
    st[i][j][o] = min(st[i][j - 1][o], st[i + bit[j - 1]][j - 1][o]);
}

inline int rmq(int l, int r, int o) {
    int lg = l_g[r - l + 1];
    return min(st[l][lg][o], st[r - bit[lg] + 1][lg][o]);
}

struct Seg {
    int v, l, r;
    friend bool operator < (Seg a, Seg b)
    { return a.v > b.v; }
};
priority_queue <Seg> q;

int main() {
    n = read();
    for(ri i = 1; i <= n; i ++) {
        int w = (v[i][(i ^ 1) & 1] = read());
        v[i][i & 1] = inf; arc[w] = i;
    }
    Init();
    q.push({ rmq(1, n, 0), 1, n });
    while(!q.empty()) {
        Seg tmp = q.top(); q.pop();
        int w1 = tmp.v, pw1 = arc[w1];
        int w2 = rmq(pw1 + 1, tmp.r, pw1 & 1), pw2 = arc[w2];
        printf("%d %d ", w1, w2);
        if(tmp.l != pw1) q.push({ rmq(tmp.l, pw1 - 1, (tmp.l + 1) & 1), tmp.l, pw1 - 1 });
        if(pw1 + 1 != pw2) q.push({ rmq(pw1 + 1, pw2 - 1, pw1 & 1), pw1 + 1, pw2 - 1 });
        if(pw2 != tmp.r) q.push({ rmq(pw2 + 1, tmp.r, pw2 & 1), pw2 + 1, tmp.r });
    }
    return 0;
}

F.Prime Flip

有$n$个位置的牌向上,

每次可以选定一段长为$p$($p$为奇质数)的区间,翻转这个区间

询问最少几次能使所有牌向下

考虑构造新序列$e_i = [a_i = a_{i + 1}]$,$a_i$表示向上还是向下

当$e_i$全部为0时,原序列全部向下

那么,原题的区间操作转化为同时对两个点翻转

两个点匹配有3种情况

1.相差为奇质数,需要1次

2.相差为偶数,需要2次

3.否则,需要3次

对于第一种情况,做二分图匹配

第二种情况,讨论

第三种情况,讨论

对我而言是神题,不会做.....

然而有一堆AK的神仙....

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define ri register int
#define ssd 10000010
#define sid 205

int n, N = 1e7;
int pr[ssd / 10], tot;
int nj, no, js[sid], os[sid];
bool nop[ssd], e[ssd];

int tim, vis[sid], mat[sid];
bool ex[sid][sid];

inline void Init() {
    nop[1] = 1;
    for(ri i = 2; i <= N + 1; i ++) {
        if(!nop[i]) pr[++ tot] = i;
        for(ri j = 1; j <= tot; j ++) {
            int nx = i * pr[j]; if(nx > N + 1) break;
            nop[nx] = 1; if(i % pr[j] == 0) break;
        }
    }
    nop[2] = 1;
}

inline int dfs(int o) {
    for(int i = 1; i <= no; i ++)
    if(ex[o][i] && vis[i] != tim) {
        vis[i] = tim;
        if(!mat[i] || dfs(mat[i])) 
        return mat[i] = o, 1;
    }
    return 0;
}

int main() {
    Init();
    cin >> n;
    for(ri i = 1; i <= n; i ++) { int x; cin >> x; e[x] = 1; }
    
    for(ri i = 1; i <= N + 1; i ++)
    if(e[i] != e[i - 1]) {
        if(i & 1) js[++ nj] = i;
        else os[++ no] = i;
    }

    for(ri i = 1; i <= nj; i ++)
    for(ri j = 1; j <= no; j ++)
    if(!nop[abs(js[i] - os[j])]) ex[i][j] = 1;

    int num = 0, ans = 0;

    for(ri i = 1; i <= nj; i ++)
    ++ tim, num += dfs(i);

    nj -= num; no -= num;
    ans = num + nj / 2 * 2 + no / 2 * 2 + (nj & 1) * 3;
    printf("%d
", ans);
    return 0;
}