作者: KSkun

[ZJOI2009]函数 题解

[ZJOI2009]函数 题解

题目地址:洛谷:【P2591】[ZJOI2009]函数 – 洛谷、BZOJ:P 

[SDOI2014]旅行 题解

[SDOI2014]旅行 题解

题目地址:洛谷:【P3313】[SDOI2014]旅行 – 洛谷、BZOJ:P 

[POI2005]BAN-Bank Notes 题解

[POI2005]BAN-Bank Notes 题解

题目地址:洛谷:【P3423】[POI2005]BAN-Bank Notes – 洛谷、BZOJ:Problem 1531. — [POI2005]Bank notes

注:截止本文发布时,洛谷上的该题仍然0AC,怀疑数据出现问题。

题目描述

Byteotian Bit Bank (BBB) 拥有一套先进的货币系统,这个系统一共有n种面值的硬币,面值分别为b1, b2,…, bn. 但是每种硬币有数量限制,现在我们想要凑出面值k求最少要用多少个硬币.

输入输出格式

输入格式:
第一行一个数 n, 1 <= n <= 200. 接下来一行 n 个整数b1, b2,…, bn, 1 <= b1 < b2 < … < b n <= 20 000, 第三行 n 个整数c1, c2,…, cn, 1 <= ci <= 20 000, 表示每种硬币的个数.最后一行一个数k – 表示要凑的面值数量, 1 <= k <= 20 000.

输出格式:
第一行一个数表示最少需要付的硬币数

输入输出样例

输入样例#1:

3
2 3 5
2 2 1
10

输出样例#1:

3
1 1 1

题解

裸的部分背包,可以做一个求最小值的部分背包即可。对于输出方案,我们可以先二进制拆分成01背包问题,再记录下01背包的转移过程,倒推统计每个物品是否被选中即可。
复杂度O(nk \log n)

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 10005, INF = 1e9;

int n, b[MAXN], c[MAXN], v[MAXN], w[MAXN], bel[MAXN], cnt[MAXN], tot, k, dp[20005];
bool vis[MAXN][20005];

int main() {
    n = readint();
    for(int i = 1; i <= n; i++) {
        b[i] = readint();
    }
    for(int i = 1; i <= n; i++) {
        c[i] = readint();
    }
    k = readint();
    for(int i = 1; i <= n; i++) {
        for(int siz = 1; c[i]; siz <<= 1) {
            siz = std::min(siz, c[i]); c[i] -= siz;
            tot++; w[tot] = siz; v[tot] = b[i] * siz; bel[tot] = i;
        }
    }
    for(int i = 1; i <= k; i++) {
        dp[i] = INF;
    }
    for(int i = 1; i <= tot; i++) {
        for(int j = k; j >= v[i]; j--) {
            if(dp[j] > dp[j - v[i]] + w[i]) {
                dp[j] = dp[j - v[i]] + w[i];
                vis[i][j] = true;
            }
        }
    }
    printf("%d\n", dp[k]);
    for(int i = tot; i >= 1; i--) {
        if(vis[i][k]) {
            cnt[bel[i]] += w[i];
            k -= v[i];
        }
    }
    for(int i = 1; i <= n; i++) {
        printf("%d ", cnt[i]);
    }
    return 0;
}
[JSOI2010]缓存交换 题解

[JSOI2010]缓存交换 题解

题目地址:洛谷:【P4404】[JSOI2010]缓存交换 – 洛谷、BZOJ 

[AHOI2008]逆序对 题解

[AHOI2008]逆序对 题解

题目地址:洛谷:【P4280】[AHOI2008]逆序对 – 洛谷、BZOJ: 

[JSOI2010]冷冻波 题解

[JSOI2010]冷冻波 题解

题目地址:洛谷:【P4048】[JSOI2010]冷冻波 – 洛谷、BZOJ:Problem 1822. — [JSOI2010]Frozen Nova 冷冻波

题目描述

WJJ喜欢“魔兽争霸”这个游戏。在游戏中,巫妖是一种强大的英雄,它的技能Frozen Nova每次可以杀死一个小精灵。我们认为,巫妖和小精灵都可以看成是平面上的点。
当巫妖和小精灵之间的直线距离不超过R,且巫妖看到小精灵的视线没有被树木阻挡(也就是说,巫妖和小精灵的连线与任何树木都没有公共点)的话,巫妖就可以瞬间杀灭一个小精灵。
在森林里有N个巫妖,每个巫妖释放Frozen Nova之后,都需要等待一段时间,才能再次施放。不同的巫妖有不同的等待时间和施法范围,但相同的是,每次施放都可以杀死一个小精灵。
现在巫妖的头目想知道,若从0时刻开始计算,至少需要花费多少时间,可以杀死所有的小精灵?

题意简述

巫妖一次可以杀死一只圆形范围内且连线与树障(圆形区域)无公共点的精灵,但是攻击有CD。求至少花费多少时间可以杀死全部精灵。

输入输出格式

输入格式:
输入文件第一行包含三个整数N、M、K(N,M,K<=200),分别代表巫妖的数量、小精灵的数量和树木的数量。
接下来N行,每行包含四个整数x, y, r, t,分别代表了每个巫妖的坐标、攻击范围和施法间隔(单位为秒)。
再接下来M行,每行两个整数x, y,分别代表了每个小精灵的坐标。
再接下来K行,每行三个整数x, y, r,分别代表了每个树木的坐标。
输入数据中所有坐标范围绝对值不超过10000,半径和施法间隔不超过20000。

输出格式:
输出一行,为消灭所有小精灵的最短时间(以秒计算)。如果永远无法消灭所有的小精灵,则输出-1。

输入输出样例

输入样例#1:

2 3 1
-100 0 100 3
100 0 100 5
-100 -10
100 10
110 11
5 5 10

输出样例#1:

5

题解

首先我们可以处理出来每个巫妖可以杀死哪些精灵,简单的实现就是判断距离是否超过以及是否跟某个树有交点。这里判交点用的方法是检查端点及叉积计算出圆心-直线距离再判断是否有交点。
得到了这个表以后,我们就可以二分答案,每次使用最大流检验,建立源→巫妖→精灵→汇的网络,巫妖→精灵→汇中间的所有边容量为1,源→巫妖的所有边容量为floor(答案/该巫妖CD)+1,代表这一次该巫妖最多杀死多少精灵。满流即代表答案可行。
复杂度玄学。

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cmath>

#include <algorithm>
#include <queue>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) 
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 1005, INF = 1e9;
const double EPS = 1e-8;

struct Edge {
    int to, cap, nxt;
} gra[MAXN << 8];
int head[MAXN], tot;

inline void addedge(int u, int v, int cap) {
    gra[tot] = Edge {v, cap, head[u]}; head[u] = tot++;
    gra[tot] = Edge {u, 0, head[v]}; head[v] = tot++;
}

int level[MAXN];

inline bool bfs(int s, int t) {
    memset(level, -1, sizeof(level));
    std::queue<int> que;
    que.push(s); level[s] = 0;
    while(!que.empty()) {
        int u = que.front(); que.pop();
        for(int i = head[u]; ~i; i = gra[i].nxt) {
            int v = gra[i].to;
            if(gra[i].cap && level[v] == -1) {
                level[v] = level[u] + 1;
                if(v == t) return true;
                que.push(v);
            }
        }
    }
    return level[t] != -1;
}

int cur[MAXN];

inline int dfs(int u, int t, int left) {
    if(u == t || !left) return left;
    int flow = 0;
    for(int &i = cur[u]; ~i; i = gra[i].nxt) {
        int v = gra[i].to;
        if(level[v] == level[u] + 1 && gra[i].cap) {
            int f = dfs(v, t, std::min(left, gra[i].cap));
            if(f) {
                flow += f; left -= f;
                gra[i].cap -= f; gra[i ^ 1].cap += f;
                if(!left) break;
            }
        }
    }
    return flow;
}

inline int dinic(int s, int t) {
    int flow = 0;
    while(bfs(s, t)) {
        memcpy(cur, head, sizeof(head));
        int f;
        while(f = dfs(s, t, INF)) flow += f;
    }
    return flow;
}

int n, m, k, S, T;
std::vector<int> elfs[MAXN];

class Pos {
public:
    int x, y;
    Pos() {}
    Pos(int x, int y): x(x), y(y) {}
};

class Litch : public Pos {
public:
    int r, t;
} lit[MAXN];

typedef Pos Elf;

Elf elf[MAXN];

class Tree : public Pos {
public:
    int r;
} tre[MAXN];

inline double dis(Pos x, Pos y) {
    return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}

inline bool inter(Pos x, Pos y, Tree tr) {
    double dis1 = dis(x, tr), dis2 = dis(y, tr), dis3 = dis(x, y);
    if(dis1 - tr.r < EPS || dis2 - tr.r < EPS) return true;
    double dis4 = fabs((x.x - y.x) * (x.y - tr.y) - (x.y - y.y) * (x.x - tr.x)) / dis3;
    double dis5 = sqrt(dis1 * dis1 - dis4 * dis4), dis6 = sqrt(dis2 * dis2 - dis4 * dis4);
    return dis5 + dis6 - dis3 < EPS && dis4 - tr.r < EPS;
}

inline bool check(int x) {
    tot = 0; memset(head, -1, sizeof(head));
    for(int i = 1; i <= n; i++) {
        addedge(S, i, x / lit[i].t + 1);
        for(int j = 0; j < elfs[i].size(); j++) {
            addedge(i, elfs[i][j] + n, 1);
        }
    }
    for(int i = 1; i <= m; i++) {
        addedge(i + n, T, 1);
    }
    return dinic(S, T) >= m;
}

int main() {
    n = readint(); m = readint(); k = readint(); S = n + m + 1; T = S + 1;
    for(int i = 1; i <= n; i++) {
        lit[i].x = readint(); lit[i].y = readint(); lit[i].r = readint(); lit[i].t = readint();
    }
    for(int i = 1; i <= m; i++) {
        elf[i].x = readint(); elf[i].y = readint();
    }
    for(int i = 1; i <= k; i++) {
        tre[i].x = readint(); tre[i].y = readint(); tre[i].r = readint();
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            if(dis(lit[i], elf[j]) - lit[i].r > EPS) continue;
            bool success = true;
            for(int jj = 1; jj <= k; jj++) {
                if(inter(lit[i], elf[j], tre[jj])) {
                    success = false; break;
                }
            }
            if(success) elfs[i].push_back(j);
        }
    }
    int l = 0, r = INF, mid;
    if(check(0)) {
        printf("0"); return 0;
    }
    while(r - l > 1) {
        mid = (l + r) >> 1;
        if(check(mid)) r = mid; else l = mid;
    }
    printf("%d", r == INF ? -1 : r);
    return 0;
}
[POI2005]SAM-Toy Cars 题解

[POI2005]SAM-Toy Cars 题解

题目地址:洛谷:【P3419】[POI2005]SAM-Toy Cars –  

[POI2005]KOS-Dicing 题解

[POI2005]KOS-Dicing 题解

题目地址:洛谷:【P3425】[POI2005]KOS-Dicing – 洛谷 

[POI2005]SKA-Piggy Banks 题解

[POI2005]SKA-Piggy Banks 题解

题目地址:洛谷:【P3420】[POI2005]SKA-Piggy Banks – 洛谷、BZOJ:Problem 1529. — [POI2005]ska Piggy banks

题目描述

Byteazar the Dragon拥有N个小猪存钱罐。每一个存钱罐能够用相应的钥匙打开或者被砸开。Byteazar已经将钥匙放入到一些存钱罐中。现在已知每个钥匙所在的存钱罐,Byteazar想要买一辆小汽车,而且需要打开所有的存钱罐。然而,他想要破坏尽量少的存钱罐,帮助Byteazar去决策最少要破坏多少存钱罐。

输入输出格式

输入格式:
第一行:包括一个整数N(1<=N<=1000000),这是Byteazar the Dragon拥有的存钱罐的数量。
存钱罐(包括它们对应的钥匙)从1到N编号。
接下来有N行:第i+1行包括一个整数x,表示第i个存钱罐对应的钥匙放置在了第x个存钱罐中。

输出格式:
仅一行:包括一个整数,表示能打开所有存钱罐的情况下,需要破坏的存钱罐的最少数量。

输入输出样例

输入样例#1:

4
2
1
2
4

输出样例#1:

2

题解

考虑把钥匙位置→存钱罐建成图上的边,显然一个强连通分量内只要一个被打开/打破,整个分量的存钱罐都可以被无损打开。因此,我们考虑先缩点,统计缩点后的DAG上入度为0的点,只需要打破这些点/分量中任意一点即可打开所有的存钱罐。
复杂度O(n)

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>

#include <algorithm>
#include <vector>
#include <stack>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 1000005;

int n, deg[MAXN];
std::vector<int> gra[MAXN];

int dfn[MAXN], low[MAXN], sno[MAXN], scc, clk;
std::stack<int> sta;
bool insta[MAXN];

inline void tarjan(int u) {
    dfn[u] = low[u] = ++clk;
    sta.push(u); insta[u] = true;
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(!dfn[v]) {
            tarjan(v);
            low[u] = std::min(low[u], low[v]);
        } else if(insta[v]) {
            low[u] = std::min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u]) {
        int p; scc++;
        do {
            p = sta.top(); sta.pop(); insta[p] = false;
            sno[p] = scc;
        } while(p != u);
    }
}

int main() {
    n = readint();
    for(int i = 1, j; i <= n; i++) {
        j = readint();
        gra[j].push_back(i);
    }
    for(int i = 1; i <= n; i++) {
        if(!dfn[i]) tarjan(i);
    }
    for(int i = 1; i <= n; i++) {
        for(int k = 0; k < gra[i].size(); k++) {
            int j = gra[i][k];
            if(sno[i] != sno[j]) {
                deg[sno[j]]++;
            }
        }
    }
    int cnt = 0;
    for(int i = 1; i <= scc; i++) {
        if(!deg[i]) cnt++;
    }
    printf("%d", cnt);
    return 0;
}
[BZOJ3343]教主的魔法 题解

[BZOJ3343]教主的魔法 题解

题目地址:洛谷:【P2801】教主的魔法 – 洛谷、BZOJ:Problem