月度归档: 2018 年 4 月

[USACO05MAR]Ombrophobic Bovines 题解

[USACO05MAR]Ombrophobic Bovines 题解

题目地址:POJ:2391 — Ombrophobic Bovines、OpenJudge百练:OpenJudge – 2391:Ombrophobic Bovines

题目描述

FJ’s cows really hate getting wet so much that the mere thought of getting caught in the rain makes them shake in their hooves. They have decided to put a rain siren on the farm to let them know when rain is approaching. They intend to create a rain evacuation plan so that all the cows can get to shelter before the rain begins. Weather forecasting is not always correct, though. In order to minimize false alarms, they want to sound the siren as late as possible while still giving enough time for all the cows to get to some shelter.
The farm has F (1 <= F <= 200) fields on which the cows graze. A set of P (1 <= P <= 1500) paths connects them. The paths are wide, so that any number of cows can traverse a path in either direction.
Some of the farm’s fields have rain shelters under which the cows can shield themselves. These shelters are of limited size, so a single shelter might not be able to hold all the cows. Fields are small compared to the paths and require no time for cows to traverse.
Compute the minimum amount of time before rain starts that the siren must be sounded so that every cow can get to some shelter.
给定一张的无向图,每条边有长度。点i处有ai头牛,以及一个能容纳bi头牛的牛棚。牛可以沿边移动,每条边上可以同时有任意头牛经过。一头牛经过一条边所需时间即为道路长度。
给出一个将牛分配到牛棚的方案,并最小化所需移动时间T。

输入输出格式

输入格式:
* Line 1: Two space-separated integers: F and P
* Lines 2..F+1: Two space-separated integers that describe a field. The first integer (range: 0..1000) is the number of cows in that field. The second integer (range: 0..1000) is the number of cows the shelter in that field can hold. Line i+1 describes field i.
* Lines F+2..F+P+1: Three space-separated integers that describe a path. The first and second integers (both range 1..F) tell the fields connected by the path. The third integer (range: 1..1,000,000,000) is how long any cow takes to traverse it.

输出格式:
* Line 1: The minimum amount of time required for all cows to get under a shelter, presuming they plan their routes optimally. If it not possible for the all the cows to get under a shelter, output “-1”.

输入输出样例

输入样例#1:

3 4
7 2
0 4
2 6
1 2 40
3 2 70
2 3 90
1 3 120

输出样例#1:

110

说明

OUTPUT DETAILS:
In 110 time units, two cows from field 1 can get under the shelter in that field, four cows from field 1 can get under the shelter in field 2, and one cow can get to field 3 and join the cows from that field under the shelter in field 3. Although there are other plans that will get all the cows under a shelter, none will do it in fewer than 110 time units.

题解

我们不好直接求这个移动时间,但是如果固定了一个时间,我们可以通过构图最大流来判断是否可行。
如果说已知了时间,我们可以将每个位置拆点,分别代表牛和牛棚,分别向源汇连边,容量控制牛的数量或牛棚的容量。牛与牛棚之间的边由最长时间来决定。我们可以预先跑一遍Floyd,处理出最短路。如果最短路并未超过二分的时间,那么就可以加边。如果代表牛的点满流,则所有牛都可以分配进牛棚里,即满足条件。
注:下面的代码在POJ上TLE了,但是可以通过OpenJudge百练的评测以及官方数据的测试。官方数据下载

代码

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

#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(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXE = 400005, MAXV = 405;
const int INF = 1e9;

struct Edge {
    int to, cap, nxt;
} gra[MAXE << 1];
int head[MAXV], tot;

inline void reset() {
    memset(head, -1, sizeof(head)); tot = 0;
}

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 n, m;

LL dis[MAXV][MAXV];

inline void floyd() {
    for(register int k = 1; k <= n; k++) {
        for(register int i = 1; i <= n; i++) {
            for(register int j = 1; j <= n; j++) {
                dis[i][j] = std::min(dis[i][j], dis[i][k] + dis[k][j]);
            }
        }
    }
}

int level[MAXV], cur[MAXV];
std::queue<int> que;

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

bool vis[MAXV];

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

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

int cnum[MAXV], snum[MAXV], csum;
int S, T;

// 1~n cow
// n+1~2n shelter

inline bool check(LL mid) {
    reset();
    for(register int i = 1; i <= n; i++) {
        addedge(S, i, cnum[i]);
        addedge(i + n, T, snum[i]);
        addedge(i, i + n, INF);
    }
    for(register int i = 1; i <= n; i++) {
        for(register int j = i + 1; j <= n; j++) {
            if(dis[i][j] <= mid) {
                addedge(i, j + n, INF);
                addedge(j, i + n, INF);
            }
        }
    }
    return dinic(S, T) >= csum;
}

int ut, vt;
LL wt;

int main() {
    n = readint(); m = readint(); S = 2 * n + 1; T = S + 1;
    for(register int i = 1; i <= n; i++) {
        cnum[i] = readint(); snum[i] = readint();
        csum += cnum[i];
    }
    memset(dis, 0x3f, sizeof(dis));
    for(register int i = 1; i <= m; i++) {
        ut = readint(); vt = readint(); wt = readint();
        dis[ut][vt] = dis[vt][ut] = std::min(dis[ut][vt], wt);
    }
    for(register int i = 1; i <= n; i++) {
        dis[i][i] = 0;
    }
    floyd();
    register LL l = 0, r = 1e16, mid;
    while(r - l > 1) {
        mid = (l + r) >> 1;
        if(check(mid)) r = mid; else l = mid;
    }
    if(r != 1e16) printf("%lld", r); else puts("-1");
    return 0;
}
[BZOJ4802]欧拉函数 题解

[BZOJ4802]欧拉函数 题解

题目地址:BZOJ:Problem 4802. — 欧拉函数

题目描述

已知N,求phi(N)

输入输出格式

输入格式:
正整数N。N<=10^18

输出格式:
输出phi(N)

输入输出样例

输入样例#1:

8

输出样例#1:

4

题解

本题需要的数学姿势有:Miller-Rabin素性测试与Pollard’s rho算法 | KSkun’s Blog
我们知道欧拉函数有一种求法是
\varphi(x) = x (1 - \frac{1}{p_1}) (1 - \frac{1}{p_2}) \cdots (1 - \frac{1}{p_k})
那分解一波质因数就可以求了。数据范围让我们用PR算法。
吐槽:std::abs被卡常了

代码

// Code by KSkun, 2018/4
#include <cstdio>
#include <cstring>
#include <ctime>
#include <cstdlib>

#include <algorithm>
#include <vector>

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;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

inline LL mul(LL a, LL b, LL p) {
    a %= p; b %= p;
    LL res = 0;
    while(b) {
        if(b & 1) {
            res += a; res %= p;
        }
        a <<= 1;
        if(a >= p) a %= p;
        b >>= 1;
    }
    return res;
}

inline LL fpow(LL n, LL k, LL p) {
    LL res = 1;
    for(; k; n = mul(n, n, p), k >>= 1) {
        if(k & 1) res = mul(res, n, p);
    }
    return res;
}

LL T, n;

inline bool miller_rabin(LL x) {
    if(x == 2) return true;
    LL t = x - 1, cnt2 = 0;
    while(!(t & 1)) {
        t >>= 1; cnt2++;
    }
    for(int i = 0; i < 20; i++) {
        LL a = rand() % (x - 2) + 2, now, lst;
        now = lst = fpow(a, t, x);
        for(int j = 1; j <= cnt2; j++) {
            now = mul(lst, lst, x);
            if(now == 1 && lst != 1 && lst != x - 1) return false;
            lst = now;
        }
        if(now != 1) return false;
    }
    return true;
}

LL mn;

inline LL gcd(LL a, LL b) {
    if(a == 0) return 1;
    while(b) {
        LL t = a % b; a = b; b = t;
    }
    return a;
}

inline LL abs(LL x) {
    return x < 0 ? -x : x;
}

inline LL pollard_rho(LL x, LL k) {
    LL a = rand() % (x - 1) + 1, b = a, t1 = 1, t2 = 2;
    for(;;) {
        t1++;
        a = (mul(a, a, x) + k) % x;
        LL g = gcd(abs(b - a), x);
        if(g > 1 && g < x) return g;
        if(b == a) return x;
        if(t1 == t2) {
            b = a;
            t2 <<= 1;
        }
    }
}

std::vector<LL> divi;

inline void caldivisor(LL x) {
    if(x == 1) return;
    if(miller_rabin(x)) {
        divi.push_back(x);
        return;
    }
    LL p = x;
    while(p >= x) p = pollard_rho(x, rand() % (x - 1) + 1);
    caldivisor(p);
    caldivisor(x / p);
}

LL N;

int main() {
    srand(time(NULL));
    N = readint();
    if(N == 1) {
        puts("1"); return 0;
    }
    if(miller_rabin(N)) {
        printf("%lld", N - 1); return 0;
    }
    caldivisor(N);
    std::sort(divi.begin(), divi.end());
    divi.erase(std::unique(divi.begin(), divi.end()), divi.end());
    LL phi = N;
    for(int i = 0; i < divi.size(); i++) {
        phi /= divi[i];
        phi *= divi[i] - 1;
    }
    printf("%lld", phi);
    return 0;
}
[SDOI2013]随机数生成器 题解

[SDOI2013]随机数生成器 题解

题目地址:洛谷:【P3306】[SDOI2013]随机数生成器 – 洛谷、BZOJ:Problem 3122. — [Sdoi2013]随机数生成器

题目描述

小W喜欢读书,尤其喜欢读《约翰克里斯朵夫》。最近小W准备读一本新书,这本书一共有P页,页码范围为0..P-1。
小W很忙,所以每天只能读一页书。为了使事情有趣一些,他打算使用NOI2012上学习的线性同余法生成一个序列,来决定每天具体读哪一页。
我们用Xi来表示通过这种方法生成出来的第i个数,也即小W第i天会读哪一页。这个方法需要设置3个参数a,b,X1,满足0<=a,b,X1<=p-1,且a,b,X1都是整数。按照下面的公式生成出来一系列的整数:Xi+1=(aXi+b) mod p其中mod表示取余操作。
但是这种方法可能导致某两天读的页码一样。
小W要读这本书的第t页,所以他想知道最早在哪一天能读到第t页,或者指出他永远不会读到第t页。

输入输出格式

输入格式:
输入含有多组数据,第一行一个正整数T,表示这个测试点内的数据组数。 接下来T行,每行有五个整数p,a,b,X1,t,表示一组数据。保证X1和t都是合法的页码。 注意:P一定为质数

输出格式:
共T行,每行一个整数表示他最早读到第t页是哪一天。如果他永远不会读到第t页,输出-1。

输入输出样例

输入样例#1:

3
7  1 1 3 3
7  2 2 2 0
7  2 2 2 1

输出样例#1:

1 
3 
-1

说明

0<=a<=P-1,0<=b<=P-1,2<=P<=10^9

题解

本题需要的数学姿势有:BSGS算法(大步小步法)及其扩展原理及应用 | KSkun’s Blog
咦这个题好像能推式子。
首先X_1 \equiv t \pmod{p}的时候答案直接就是1。
然后考虑一下X_i的通项其实就是底下这个
X_i = [a^{i-1}X_1 + b(1 + a + \cdots + a^{i-2})] \bmod p
那也就是说我要求使下面这个成立的最小的i。
a^{i-1}X_1 + b(1 + a + \cdots + a^{i-2}) \equiv t \pmod{p}
用等比数列求和公式啊!
a^{i-1}X_1 + b \cdot \frac{1-a^{i-1}}{1-a} \equiv t \pmod{p}
乱搞一通
a^{i-1} \equiv \frac{t(1-a)-b}{X_1(1-a)-b} \pmod{p}
直接一个BSGS套上去。
诶还是全WA了?GG,开始拍。
噫漏了a=1的情况,来推一下a=1的情况
X_1 + b(i-1) \equiv t \pmod{p}
这个移项以后i就出来了。
等一下等一下,万一b=0呢?特判搞一搞。
再等一下,万一a=0呢?还得特判搞一搞。
这些写全了才能A。
吐槽:HashMap的模数改成19260817以后卡的飞起啊!

代码

// Code by KSkun, 2018/4
#include <cstdio>
#include <cmath>
#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;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

inline LL fpow(LL n, LL k, LL p) {
    LL res = 1;
    for(; k; n = n * n % p, k >>= 1) {
        if(k & 1) res = res * n % p;
    }
    return res;
}

const int MO = 611977, MAXN = 1000005;

struct HashMap {
    int head[MO + 5], key[MAXN], value[MAXN], nxt[MAXN], tot;
    inline void clear() {
        tot = 0;
        memset(head, -1, sizeof(head));
    }
    HashMap() {
        clear();
    }
    inline void insert(int k, int v) {
        int idx = k % MO;
        for(int i = head[idx]; ~i; i = nxt[i]) {
            if(key[i] == k) {
                value[i] = v;
                return;
            }
        }
        key[tot] = k; value[tot] = v; nxt[tot] = head[idx]; head[idx] = tot++;
    }
    inline int operator[](const int &k) const {
        int idx = k % MO;
        for(int i = head[idx]; ~i; i = nxt[i]) {
            if(key[i] == k) return value[i];
        }
        return -1;
    }
} x;

inline LL bsgs(LL a, LL b, LL p) {
    if(b == 1) return 0;
    LL m = ceil(sqrt(p + 0.5)), inv = fpow(a, p - m - 1, p);
    x.clear();
    x.insert(1, 0);
    for(LL i = 1, e = 1; i < m; i++) {
        e = e * a % p;
        if(x[e] == -1) x.insert(e, i);
    }
    for(LL i = 0; i < m; i++) {
        if(x[b] != -1) {
            LL res = x[b];
            return i * m + res;
        }
        b = b * inv % p;
    }
    return -1;
}

LL T, p, a, b, x1, t;

int main() {
    T = readint();
    while(T--) {
        p = readint(); a = readint(); b = readint(); x1 = readint(); t = readint();
        if(x1 == t) {
            puts("1");
            continue;
        }
        if(a == 0) { // a=0特判
            if(b == t) puts("2");
            else puts("-1");
            continue;
        }
        if(a == 1) { // a=1特判
            LL res = (((t - x1) * fpow(b, p - 2, p) % p + 1) % p + p) % p;
            if(b == 0) puts("-1");
            else printf("%lld\n", res ? res : p);
            continue;
        }
        LL B = ((t * (1 - a) % p - b) % p + p) % p, BI = ((x1 * (1 - a) % p - b) % p + p) % p;
        B = B * fpow(BI, p - 2, p) % p;
        LL res = bsgs(a, B, p);
        printf("%lld\n", res == -1 ? -1 : res + 1);
    }
    return 0;
}