POI2007 题解

POI2007 完结撒花!

旅游景点atr

很简单的一道 dp 题,不说了。有意思的是官网上的标程把空间复杂度压成了 \( 2^k \),而我不知道这是怎么搞的,所以官网上提交会 \( MLE \)

办公楼biu

很容易想到求补图的联通块,然后随便敲了个 \( BFS \),果断 \( WA \) 

考虑用并查集把相同联通块的点圈起来,类似缩点的思想,这样一边枚举点一边减少点,可以卡着过

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5+5;
const int M = 2e6+5;
vector<int> g[N];
int n, m, a, b;
int col[N], cnt[N];
int fa[N];

int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

int main() {
    scanf("%d %d", &n, &m);
    for (int _ = 1; _ <= m; _++) {
        scanf("%d %d", &a, &b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    for (int i = 1; i <= n+1; i++) {
        fa[i] = i;
    }
    int ans = 0;
    for (int i = 1; i <= n; i = find(i+1)) {
        queue<int> q;
        q.push(i);
        cnt[++ans] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            fa[u] = find(u+1);
            for (int i = 0; i < g[u].size(); i++) {
                col[g[u][i]] = u;
            }
            for (int i = find(1); i <= n; i = find(i+1)) {
                if (col[i] != u) {
                    fa[i] = find(i+1);
                    cnt[ans]++;
                    q.push(i);
                }
            }
        }
    }
    printf("%d\n", ans);
    sort(cnt+1, cnt+1+ans);
    for (int i = 1; i <= ans; i++) {
        printf("%d ", cnt[i]);
    }
    return 0;
}
细节见代码

树Drz

这题据说要用 9 个线段树,所以我没做。。。

对称轴osi

啊。。。这题初看不可捉的样子,然而千古神犇 vfk 说对称轴平分 360 角,利用这个结论就好捉了

当然类似无序运动那题搞个字符串也可以的(然而这两种方法都没有实现出来,太懒啦QWQ)

Zap

(好奇为什么没有中文)

(就叫它密码好了)

这题用到了神仙莫比乌斯函数的性质哦 QWQ

还顺带用到了分块

式子懒得敲咯,网上一大堆

代码留着吧,有点小技巧在里面的

#include <bits/stdc++.h>

using namespace std;

const int N = 5e4+1;
int prime[N];
int mu[N], sum[N];
int t, n, m, d, ct;
bool not_prime[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    mu[1] = 1;
    for (int i = 2; i < N; i++) {
        if (!not_prime[i]) {
            prime[++ct] = i;
            mu[i] = -1;
        }
        for (int j = 1; j <= ct && i * prime[j] < N; j++) {
            not_prime[i * prime[j]] = true;
            if (i % prime[j] == 0) {
                mu[i * prime[j]] = 0;
                break;
            }
            mu[i * prime[j]] = -mu[i];
        }
    }
    for (int i = 1; i < N; i++) {
        sum[i] = sum[i-1] + mu[i];
    }
    cin >> t;
    while (t--) {
        cin >> n >> m >> d;
        if (n > m) {
            swap(n, m);
        }
        n /= d, m /= d;
        int ans = 0;
        for (int i = 1, j = 1; i <= n; i = j + 1) {
            j = min(n / (n / i), m / (m / i));
            ans += (sum[j] - sum[i-1]) * (n / i) * (m / i);
        }
        cout << ans << endl;
    }
    return 0;
}
95%通过

可惜不知道为什么 \( TLE \) 了一个点 QWQ

山峰和山谷Grz

试机水题

大都市meg

据说可用 dfs + 树状数组水过

洪水pow

据说可用 dfs + 并查集水过

石头花园SKA

好神奇的推论题哦

从没想过要把石头放在同一边

而且也没想过要分四种情况

立方体大作战tet

贪心搞一搞咯

驾驶考试egz

呀,想歪了一点点

大概就是求两个 dp 数组然后滑动窗口搞一搞的咯

网上题解漫天飞

只留个还算美观的代码吧

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5+5;

int c[N];
int n, m, p, k, x, h, d;
vector<int> pth[2][N], lis[2][N];
int bit[N], lf[N], rf[N];

int lowbit(int x) {
    return x & -x;
}

int ask(int p) {
    int ret = 0;
    for (; p > 0; p -= lowbit(p)) {
        ret = max(ret, bit[p]);
    }
    return ret;
}

void change(int p, int v) {
    for (; p <= m; p += lowbit(p)) {
        bit[p] = max(bit[p], v);
    }
}

void run(int s, int d, int t, int *f) {
    int ls = 0;
    memset(bit, 0, sizeof(int)*(m+1));
    for (int i = s, j = 2; j <= n; j++, i += d) {
        for (int k = 0; k < pth[t][i].size(); k++) {
            int ak = ask(pth[t][i][k])+1;
            ls = max(ls, ak);
            lis[t][i].push_back(ak);
        }
        for (int k = 0; k < lis[t][i].size(); k++) {
            change(pth[t][i][k], lis[t][i][k]);
        }
        f[i] = !t ? i - 1 - ls : n - i - ls;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> p >> k;
    m++;
    for (int i = 1; i <= p; i++) {
        cin >> x >> h >> d;
        pth[d^1][x+d].push_back(m-h);
    }
    run(2, 1, 0, lf);
    run(n-1, -1, 1, rf);
    int ans = 0, buf = 0;
    for (int i = 1, j = 1; i <= n; i++) {
        while (j <= n && lf[j] + rf[i] <= k) {
            j++;
        }
        ans = max(ans, j - i);
        if (!lf[i] && !rf[i]) {
            buf++;
        }
    }
    cout << ans - buf << endl;
    return 0;
}
丑丑的代码

天然气管道Gaz

呃。。。实在佩服 POI 的题目

水题硬生生想破脑袋

堆积木Klo

哇。。。听说这类问题有个很 NB 的名字叫二维偏序的。。。

当然还有大名鼎鼎的三维偏序。。。

dp 式子省略咯(反正网上满篇飞)

砝码Odw

orz 又是贪心,而且貌似很显然的样子

搞搞进位制就好了

四进制的天平Wag

呀,这个 dp 有意思哦

首先有几个贪心的推论要了解的

然后就可以大胆的 dp 啦(没错我还是抄的别人的代码)(所以不敢贴出来)

可怜的我只会推结论不会写 dp 

而且结论貌似也很显然。。。

小结

每道不可捉的题背后都有一个可捉的推论。

猜你喜欢

转载自www.cnblogs.com/HailJedi/p/9265575.html