最新文章

[SDOI2015]星际战争 题解

[SDOI2015]星际战争 题解

题目地址:洛谷:【P3324】[SDOI2015]星际战争 – 洛谷、BZOJ 

[USACO15DEC]最大流Max Flow 题解

[USACO15DEC]最大流Max Flow 题解

题目地址:洛谷:【P3128】[USACO15DEC]最大流Max Flow &#8211 

[SCOI2007]修车 题解

[SCOI2007]修车 题解

题目地址:洛谷:【P2053】[SCOI2007]修车 – 洛谷、BZOJ:Problem 1070. — [SCOI2007]修车

题目描述

同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。
说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

输入输出格式

输入格式:
第一行有两个数M,N,表示技术人员数与顾客数。
接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。

输出格式:
最小平均等待时间,答案精确到小数点后2位。

输入输出样例

输入样例#1:

2 2
3 2
1 4

输出样例#1:

1.50

说明

(2<=M<=9,1<=N<=60), (1<=T<=1000)

题解

考虑使用最小费用最大流。我们需要考虑1.车的顺序2.修车的人,那么考虑把两个状态放在一起拆点,即让某个点代表顺序第几位修车人是谁的含义。这样,实际上我们就是在求二分图的最小权匹配了。权值的计算要考虑到这个位置对答案的贡献,显然倒数第i个修车的时间会对i个人的等待时间产生贡献,因此不妨把状态转成倒数第几位修车人是谁,这样权值就变成了产生贡献的人数*修车时间。
注意这里这些工人是可以同时开工的,也就是说,可以把车对应到同一时间的不同工人上。

代码

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

#include <vector>
#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 int readint() {
    register int 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 MAXN = 100005, INF = 1e9;

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

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

int flow, cost, pre[MAXN], pree[MAXN], f[MAXN], dis[MAXN];
bool inque[MAXN];
std::queue<int> que;

inline void mcmf(int s, int t) {
    for(;;) {
        memset(f, 0, sizeof(f));
        memset(dis, 0x3f, sizeof(dis));
        dis[s] = 0; f[s] = INF; inque[s] = true; que.push(s);
        while(!que.empty()) {
            int u = que.front(); que.pop(); inque[u] = false;
            for(int i = head[u]; ~i; i = gra[i].nxt) {
                int v = gra[i].to;
                if(gra[i].cap > 0 && dis[v] > dis[u] + gra[i].cost) {
                    pre[v] = u; pree[v] = i;
                    f[v] = std::min(f[u], gra[i].cap);
                    dis[v] = dis[u] + gra[i].cost;
                    if(!inque[v]) {
                        inque[v] = true; que.push(v);
                    }
                }
            }
        }
        if(f[t] == 0) break;
        for(int i = t; i != s; i = pre[i]) {
            gra[pree[i]].cap -= f[t]; gra[pree[i] ^ 1].cap += f[t];
        }
        flow += f[t]; cost += f[t] * dis[t];
    }
}

int n, m, t[70][70], S, T;

// 1~n car
// n+1~mn+n staff
// mn+n+1 s mn+n+2 t
int main() {
    memset(head, -1, sizeof(head));
    m = readint(); n = readint();
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            t[i][j] = readint();
        }
    }
    S = m * n + n + 1; T = S + 1;
    for(int i = 1; i <= n; i++) {
        addedge(S, m * n + i, 1, 0);
    }
    for(int i = 1; i <= m; i++) { // which worker
        for(int j = 1; j <= n; j++) { // which time
            int p = (i - 1) * n + j;
            for(int k = 1; k <= n; k++) { // which car
                addedge(m * n + k, p, 1, j * t[k][i]);
            }
            addedge(p, T, 1, 0);
        }
    }
    mcmf(S, T);
    printf("%.2lf", cost / double(n));
    return 0;
}
[洛谷3380]【模板】二逼平衡树(树套树) 题解

[洛谷3380]【模板】二逼平衡树(树套树) 题解

题目地址:洛谷:【P3380】【模板】二逼平衡树(树套树) – 洛谷、BZOJ 

[JSOI2011]任务调度 题解

[JSOI2011]任务调度 题解

题目地址:BZOJ:Problem 5179. — [Jsoi2011]任务调 

[CQOI2015]任务查询系统 题解

[CQOI2015]任务查询系统 题解

题目地址:洛谷:【P3168】[CQOI2015]任务查询系统 – 洛谷、BZOJ:Problem 3932. — [CQOI2015]任务查询系统

题目描述

最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分。超级计算机中的任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行),其优先级为Pi。同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同。调度系统会经常向查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个)的优先级之和是多少。特别的,如果Ki大于第Xi秒正在运行的任务总数,则直接回答第Xi秒正在运行的任务优先级之和。上述所有参数均为整数,时间的范围在1到n之间(包含1和n)。

输入输出格式

输入格式:
输入文件第一行包含两个空格分开的正整数m和n,分别表示任务总数和时间范围。接下来m行,每行包含三个空格分开的正整数Si、Ei和Pi(Si<=Ei),描述一个任务。接下来n行,每行包含四个空格分开的整数Xi、Ai、Bi和Ci,描述一次查询。查询的参数Ki需要由公式 Ki=1+(Ai*Pre+Bi) mod Ci计算得到。其中Pre表示上一次查询的结果,对于第一次查询,Pre=1。

输出格式:
输出共n行,每行一个整数,表示查询结果。

输入输出样例

输入样例#1:

4 3
1 2 6
2 3 3
1 3 2
3 3 4
3 1 3 2
1 1 3 4
2 2 4 3

输出样例#1:

2
8
11

说明

样例解释
K1 = (1*1+3)%2+1 = 1
K2 = (1*2+3)%4+1 = 2
K3 = (2*8+4)%3+1 = 3
对于100%的数据,1<=m,n,Si,Ei,Ci<=100000,0<=Ai,Bi<=100000,1<=Pi<=10000000,Xi为1到n的一个排列

题解

这个题强制在线了,在线做这种问题就需要主席树了。
我们先离散化优先度,在每个任务开始的时候向主席树中插入优先度,结束以后删除优先度。由于查询针对一个时间,直接用该时间的根来查就好了。

代码

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

#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;
}

const int MAXN = 100005;

int n, m;
std::vector<LL> vec;

#define mid ((l + r) >> 1)

struct Node {
    int lch, rch, siz, id;
    LL sum;
} sgt[MAXN << 6];
int rt[MAXN], tot;

inline void insert(int &o, int l, int r, int x, int id) {
    if(sgt[o].id != id) {
        sgt[++tot] = sgt[o]; o = tot; sgt[o].id = id;
    }
    sgt[o].siz++;
    sgt[o].sum += vec[x];
    if(l == r) return;
    if(x <= mid) insert(sgt[o].lch, l, mid, x, id);
    else insert(sgt[o].rch, mid + 1, r, x, id);
}

inline void erase(int &o, int l, int r, int x, int id) {
    if(sgt[o].id != id) {
        sgt[++tot] = sgt[o]; o = tot; sgt[o].id = id;
    }
    sgt[o].siz--;
    sgt[o].sum -= vec[x];
    if(l == r) return;
    if(x <= mid) erase(sgt[o].lch, l, mid, x, id);
    else erase(sgt[o].rch, mid + 1, r, x, id);
}

inline LL query(int o, int l, int r, int rk) {
    if(sgt[o].siz <= rk) return sgt[o].sum;
    if(l == r) return sgt[o].sum / sgt[o].siz * rk;
    int siz = sgt[sgt[o].lch].siz;
    LL res = 0;
    res += query(sgt[o].lch, l, mid, rk);
    if(rk > siz) res += query(sgt[o].rch, mid + 1, r, rk - siz);
    return res;
}

struct Task {
    LL s, t, prio;
} t1[MAXN], t2[MAXN];

inline bool cmp1(Task a, Task b) {
    return a.s < b.s;
}

inline bool cmp2(Task a, Task b) {
    return a.t < b.t;
}

LL k, x, a, b, c, pre = 1;

int main() {
    m = readint(); n = readint();
    for(int i = 1; i <= m; i++) {
        t1[i].s = t2[i].s = readint();
        t1[i].t = t2[i].t = readint();
        vec.push_back(t1[i].prio = t2[i].prio = readint());
    }
    vec.push_back(0);
    std::sort(vec.begin(), vec.end());
    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
    std::sort(t1 + 1, t1 + m + 1, cmp1);
    std::sort(t2 + 1, t2 + m + 1, cmp2);
    int i1 = 1, i2 = 1;
    for(int i = 1; i <= n; i++) {
        rt[i] = rt[i - 1];
        for(; t1[i1].s == i; i1++) {
            insert(rt[i], 1, vec.size() - 1, 
                std::lower_bound(vec.begin(), vec.end(), t1[i1].prio) - vec.begin(), i);
        }
        for(; t2[i2].t == i - 1; i2++) {
            erase(rt[i], 1, vec.size() - 1, 
                std::lower_bound(vec.begin(), vec.end(), t2[i2].prio) - vec.begin(), i);
        }
    }
    for(int i = 1; i <= n; i++) {
        x = readint(); a = readint(); b = readint(); c = readint();
        k = (a * pre + b) % c + 1;
        printf("%lld\n", pre = query(rt[x], 1, vec.size() - 1, k));
    }
    return 0;
}
[SDOI2009]HH的项链 题解

[SDOI2009]HH的项链 题解

题目地址:洛谷:【P1972】[SDOI2009]HH的项链 – 洛谷、BZO 

[SCOI2014]方伯伯的OJ 题解

[SCOI2014]方伯伯的OJ 题解

题目地址:洛谷:【P3285】[SCOI2014]方伯伯的OJ – 洛谷、BZ 

[ZJOI2007]报表统计 题解

[ZJOI2007]报表统计 题解

题目地址:洛谷:【P1110】[ZJOI2007]报表统计 – 洛谷、BZOJ:Problem 1058. — [ZJOI2007]报表统计

题目描述

Q的妈妈是一个出纳,经常需要做一些统计报表的工作。今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一。
经过仔细观察,小Q发现统计一张报表实际上是维护一个非负整数数列,并且进行一些查询操作。
在最开始的时候,有一个长度为N的整数序列,并且有以下三种操作:
INSERT i k:在原数列的第i个元素后面添加一个新元素k;如果原数列的第i个元素已经添加了若干元素,则添加在这些元素的最后(见下面的例子)
MIN_GAP:查询相邻两个元素的之间差值(绝对值)的最小值
MIN_SORT_GAP:查询所有元素中最接近的两个元素的差值(绝对值)
例如一开始的序列为5 3 1
执行操作INSERT 2 9将得到:5 3 9 1
此时MIN_GAP为2,MIN_SORT_GAP为2。
再执行操作INSERT 2 6将得到:5 3 9 6 1
注意这个时候原序列的第2个元素后面已经添加了一个9,此时添加的6应加在9的后面。这个时候MIN_GAP为2,MIN_SORT_GAP为1。
于是小Q写了一个程序,使得程序可以自动完成这些操作,但是他发现对于一些大的报表他的程序运行得很慢,你能帮助他改进程序么?

输入输出格式

输入格式:
第一行包含两个整数N,M,分别表示原数列的长度以及操作的次数。
第二行为N个整数,为初始序列。
接下来的M行每行一个操作,即“INSERT i k”,“MIN_GAP”,“MIN_SORT_GAP”中的一种(无多余空格或者空行)。

输出格式:
对于每一个“MIN_GAP”和“MIN_SORT_GAP”命令,输出一行答案即可。

输入输出样例

输入样例#1:

3 5
5 3 1
INSERT 2 9
MIN_SORT_GAP
INSERT 2 6
MIN_GAP
MIN_SORT_GAP

输出样例#1:

2
2
1

说明

对于30%的数据,N ≤ 1000 , M ≤ 5000
对于100%的数据,N , M ≤500000
对于所有的数据,序列内的整数不超过5×10^8。

题解

我们只需要记下原数列中某元素对应的序列的头与尾以及全局和差分分别开一个multiset就能解决本题。
插入一个元素的时候,用该元素与插入序列的尾及后一个序列的头的差更新差分multiset,并且从全局multiset中找到lower_bound位置,计算MIN_SORT_GAP即可。

代码

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

#include <algorithm>
#include <set>

inline int abs(int x) {
    return x >= 0 ? x : -x;
}

inline int min(int a, int b) {
    return a < b ? a : b;
}

const int MAXN = 500005, INF = 1e9;

int n, m, x, k, msg = INF;
char op[30];
int a[MAXN][2];
std::multiset<int> glo, del;

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &x);
        a[i][0] = a[i][1] = x;
        glo.insert(x);
        if(i > 1) del.insert(abs(a[i][0] - a[i - 1][0]));
    }
    for(std::multiset<int>::iterator it = ++glo.begin(); it != glo.end(); it++) {
        msg = min(msg, *it - *--it); it++;
    }
    while(m--) {
        scanf("%s", op);
        if(op[0] == 'I') {
            scanf("%d%d", &x, &k);
            if(x < n) del.erase(del.find(abs(a[x][1] - a[x + 1][0])));
            del.insert(abs(k - a[x][1]));
            a[x][1] = k;
            if(x < n) del.insert(abs(a[x][1] - a[x + 1][0]));
            std::multiset<int>::iterator pre = glo.lower_bound(k);
            if(pre != glo.end()) msg = min(msg, *pre - k);
            if(pre != glo.begin()) msg = min(msg, k - *--pre);
            glo.insert(k);
        } else if(op[4] == 'G') {
            printf("%d\n", *del.begin());
        } else {
            printf("%d\n", msg);
        }
    }
    return 0;
}
[ZJOI2006]书架 题解

[ZJOI2006]书架 题解

题目地址:洛谷:【P2596】[ZJOI2006]书架 – 洛谷、BZOJ:P