2017 ACM-ICPC Hua-Lien Regional

A: Smooth Sequences

如果一个序列是光滑的仅当相邻元素差的绝对值不超过d。称一个序列是半光滑的仅当修改至多一个元素使得序列变得光滑。给定序列询问是否光滑。

题解

半光滑的有3种情况,一种是连续两端差均不满足d,这种判断两端点差不超过2d即可,一种是一端的差不满足d,随便改,一种是中间某段不满足d,判断修改左右端点即可。

#include <bits/stdc++.h>
using namespace std;

int a[1005], n;

bool check_smooth()
{
    for (int i = 2; i <= n; ++i)
        if (abs(a[i] - a[i - 1]) > d)
            return false;
    return true;
}

bool check_semi_smooth()
{
    vector<int> seg;
    for (int i = 2; i <= n; ++i)
        if (abs(a[i] - a[i - 1]) > d)
            seg.push_back(i);
    if (seg.size() > 2) return false;
    if (seg.size() == 2)
    {
        if (seg[0] + 1 != seg[1]) return false; // 连续两段
        return abs(a[seg[0] - 1] - a[seg[1]]) <= d * 2;
    }
    else
    {
        if (seg[0] == 2 || seg[0] == n) return true; // 断点随便改
        return abs(a[seg[0] - 2] - a[seg[0]]) <= d * 2 || abs(a[seg[0] - 1] - a[seg[0] + 1]) <= d * 2;
    }
}

int main()
{
    int i, d;
    while (scanf("%d", &n) == 1 && n)
    {
        scanf("%d", &d);
        for (i = 1; i <= n; ++i) scanf("%d", &a[i]);
        if (check_smooth() || check_semismooth())
            puts("Y");
        else
            puts("N");
    }
    return 0;
}

B: Threesome

给定无向图,取3个点,两两直接相连,且相连边和最大。

题解

暴力

#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)

int c[305][305];

int main()
{
    int T, i, j, k, n, m, a, b, d, ans;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        m = n * (n - 1) / 2;
        ans = 0;
        FOR(i,1,m)
        {
            scanf("%d%d%d", &a, &b, &d);
            c[a][b] = c[b][a] = d;
        }
        FOR(i,1,n) FOR(j,i+1,n) FOR(k,j+1,n)
            ans = max(ans, c[i][j] + c[i][k] + c[j][k]);
        printf("%d\n", ans);
    }
    return 0;
}

C: Coefficient Computation

计算 C ( i , j ) d 进制下的表示。

题解

Java大法好。
虽然C写并不会更长,但Java无脑呀233。

import java.util.*;
import java.math.*;
import java.io.*;

public class Main {

    public static void main(String[] args) {
        BigInteger[][] C = new BigInteger[301][];
        for (int i = 0; i <= 300; ++i)
            C[i] = new BigInteger[301];
        for (int i = 0; i <= 300; ++i)
            for (int j = 0; j <= 300; ++j)
                C[i][j] = BigInteger.valueOf(0);

        for (int i = 0; i <= 300; ++i) {
            C[i][0] = C[i][i] = BigInteger.valueOf(1);
            for (int j = 1; j < i; ++j)
                C[i][j] = C[i - 1][j - 1].add(C[i - 1][j]);
        }

        Scanner scan = new Scanner(System.in);
        int T = scan.nextInt();
        for (int i = 0; i < T; ++i) {
            int n = scan.nextInt(), k = scan.nextInt(), d = scan.nextInt();
            System.out.println(C[n][k].toString(d));
        }
    }
}

D: Network Report

输出长度为x的最短路有多少条。

题解

Floyd暴力即可

#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int N = 300;
const int inf = 0x3f3f3f3f;

int dis[N][N];
int sum[N];

int main()
{
    int i, j, n, m, a, b;
    while (scanf("%d", &n) == 1 && n)
    {
        scanf("%d", &m);

        FOR(i,1,n) FOR(j,1,n) dis[i][j] = inf;
        FOR(i,1,n) dis[i][i] = 0;
        FOR(i,1,m)
        {
            scanf("%d%d", &a, &b);
            ++a; ++b;
            dis[a][b] = dis[b][a] = 1;
        }
        FOR(k,1,n) FOR(i,1,n) FOR(j,1,n)
            if (dis[i][j] > dis[i][k] + dis[k][j])
                dis[i][j] = dis[i][k] + dis[k][j];

        FOR(i,1,n) sum[i] = 0;
        FOR(i,1,n) FOR(j,1,n)
            if (i != j && dis[i][j] < inf) ++sum[dis[i][j]];

        FOR(i,1,n) if (sum[i])
            printf("%d %d\n", i, sum[i]);
    }
    return 0;
}

F: Magic Sequence Pairs

可以用网络流做,但其实没有必要的。

G: Family Gathering at Christmas

线段树

I: Tracer Deployment

二分图匹配

K: Assigning Frequencies

不超过85个数据,给出不超过25个点的图,问是否存在一个方案使得将所有点染成3种颜色且相邻点颜色不同。

题解

显然是爆搜。

#include <bits/stdc++.h>
using namespace std;
int c[100], n;
bool G[30][30];
bool dfs(int v) {
    for (int u = 0; u < v; ++u)
        if (G[u][v] && c[u] == c[v])
            return false;
    if (v == n - 1) return true;
    for (int i = 0; i < 3; ++i) {
        c[v + 1] = i;
        if (dfs(v + 1)) return true;
    }
    return false;
}

int main() {
    int T, a, b, m, i;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        memset(G, 0, sizeof G);
        for (i = 0; i < m; ++i) {
            scanf("%d%d", &a, &b);
            G[a][b] = G[b][a] = true;
        }

        if (dfs(0)) puts("Y");
        else puts("N");
    }
    return 0;
}

L: Finding the Bases

给定一个字符串,通过切割成多个字符串提取循环节的方式压缩它,并使得提取出的循环节总长度最小。

题解

看到数据范围。。。 O ( n 2 ) 居然可以过,没意思没意思。
众所周知,KMP可以求循环节,也即是 i n e x t [ i ] ,所以求n次next数组。
我们令 r i , j 表示区间 [ i , j ] 的循环节长度最小是多少。 f j 表示对于 [ 1.. j ] ,循环节最小长度是多少。
那么有 f j = min { f i 1 + r i , j } r i , j = ( j i + 1 ) n x t j i + 1

#include <bits/stdc++.h>
#define FOR(i,j,k) for(i=j;i<=k;++i)
using namespace std;
const int N = 10005, inf = 0x3f3f3f3f;
int nxt[N], r[N][N], f[N];
char s[N];

void get_next(const char *s) {
    int len = strlen(s);
    nxt[0] = -1;
    for (int i = 1, j; i < len; ++i) {
        j = nxt[i - 1];
        while (j != -1 && s[j + 1] != s[i]) j = nxt[j];
        if (s[j + 1] == s[i]) nxt[i] = j + 1;
        else nxt[i] = -1;
    }
}

int main() {
    int T, i, j, n;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%s", s + 1);
        n = strlen(s + 1);

        f[0] = 0;
        FOR(i,1,n) f[i] = inf;

        FOR(i,1,n)
        {
            get_next(s + i);
            FOR(j,1,n)
            {
                int len = (j - i) - nxt[j - i];
                if ((nxt[j - i] + 1) % len == 0) r[i][j] = len;
                else r[i][j] = j - i + 1;
                f[j] = min(f[j], f[i - 1] + r[i][j]);
            }
        }

        printf("%d\n", f[n]);
    }
    return 0;
}

M: Annual Congress of MUD

嘉年华一共有D天,每个人在 [ x i , y i ] 这几天来玩,每天都有一次盛会,但一个人必须且仅能参加一次盛会,我们希望盛会人数最多的时候最少,这样可以平衡盛会出席的人数。现在给定n个人按顺序申请来玩的时间,如果某个人申请来玩后盛会人数最多的时候是否会增加,如果是输出这个人的编号。

题解

对于前i个人我们都要知道局部的最优解。判断是否比i-1个人的情况多1(差值不可能比1还大)。那么我们跑最大流即可。源点连向人,容量为1,人连向能去的那些天,容量为1,每天连向汇点,容量为盛会人数最多可能多少人。这个多少人如果我们只要求全局的最优,那么二分即可。否则如果不满流,连向汇点的边容量+1就一定能满流了。注意这样连边显然会TLE,因为边数有 N D + N + D 条,点数也有 N + D + 2 个。我们修改构图,鉴于整个图容量为1的边很多,我们直接构造D*D个点表示区间,区间连向包含的天。那么我们每次加人,就相当于源点连向某个区间的边的容量+1,这样图就只有 D × D + D + 2 个点了。

#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int inf = 0x3f3f3f3f, N = 11000, M = 450000;
int level[N], cnt = 1, cur[N], v[M], p[M], h[N], q[M], s = 0, t, w[M];
int filled[N], x[N], y[N], id[25][25];
void add(int a, int b, int c) {
    p[++cnt] = h[a]; v[cnt] = b; w[cnt] = c; h[a] = cnt;
    p[++cnt] = h[b]; v[cnt] = a; w[cnt] = 0; h[b] = cnt;
}

bool bfs() {
    int f = 0, r = 0, u, i;
    for (i = s; i <= t; ++i) level[i] = -1;
    q[r++] = s; level[s] = 1;
    while (f < r) {
        u = q[f++];
        for (i = h[u]; i; i = p[i]) {
            if (w[i] && level[v[i]] == -1) {
                level[v[i]] = level[u] + 1;
                q[r++] = v[i];
            }
        }
    }
    return level[t] > 0;
}

int dfs(int u, int low) {
    int i, tmp = 0, res = 0;
    if (u == t) return low;
    for (i = cur[u]; i && res < low; i = p[i]) {
        if (w[i] && level[v[i]] == level[u] + 1) {
            tmp = dfs(v[i], min(w[i], low - res));
            w[i] -= tmp; w[i ^ 1] += tmp; res += tmp;
            if (w[i]) cur[u] = i;
        }
    }
    if (!res) level[u] = -1;
    return res;
}

int dinic() {
    int ans = 0, i;
    while (bfs()) {
        for (i = s; i <= t; ++i) cur[i] = h[i];
        ans += dfs(s, inf);
    }
    return ans;
}

int main() {
    int i, j, k, l, r, mid, n, d, ans;
    while (scanf("%d", &n) == 1 && n)
    {
        scanf("%d", &d);
        vector<int> e, res;
        s = 0; t = n;
        cnt = 1;
        memset(h, 0, sizeof h);
        for (i = 1; i <= n; ++i)
        {
            filled[i] = 0;
            scanf("%d%d", &x[i], &y[i]);
            add(s, i, 1);
        }

        for (i = 1; i <= d; ++i)
            for (j = i; j <= d; ++j)
                id[i][j] = ++t;

        for (i = 1; i <= d; ++i)
            for (j = i; j <= d; ++j)
                for (k = i; k <= j; ++k)
                    add(id[i][j], t + k, 1);

        for (i = 1; i <= d; ++i)
        {
            add(t + i, t + d + 1, 0);
            e.push_back(cnt - 1);
        }
        t = t + d + 1;

        int mx = 0, flow = 0;
        for (i = 1; i <= n; ++i)
        {
            add(i, id[x[i]][y[i]], inf);

            do
            {
                flow += dinic();
                if (flow == i)
                    break;
                else
                    for (auto j : e)
                        w[j]++;
            } while (1);

            int x = -1;
            for (j = h[i]; j; j = p[j])
                if (v[j] > n && w[j ^ 1])
                {
                    x = v[j] - n;
                    break;
                }
            if (x == -1) continue;
            filled[x]++;
            if (filled[x] > mx)
            {
                res.push_back(i);
                mx = filled[x];
            }
        }

        for (i = 0; i < res.size(); ++i)
            printf("%d%c", res[i], i + 1 == res.size() ? '\n' : ' ');
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huanghongxun/article/details/80157935