2016 Multi-University Training Contest 9

1002:Best Division

/*
    题意:本题的题意是给你N个数字,让你将这些数组分成尽可能多的段, 每段数字不多于L个,且这一段的异或值小于X。
    tag: dp + Trie树优化
    分析:我们可以很容易的写出一个n^2 dp, 定义dp[i]为以i结尾的最长分段, 那么 dp[i] = max(dp[j]) + 1  i-j <= L 且s[i] ^ s[j] <= X, s[i] 为
    前缀异或和。 这样就可以使用Trie树来加速状态转移的过程, 计算dp[i]的时候我们将dp[i-1] - dp[i-L+1]的值扔进Trie树中然后在这些值中
    找最大值即可。

*/

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>

using namespace std;
typedef long long LL;
const int maxn = 100000 + 100;
const int NN = 1000000 + 100;
const LL m = 268435456;
const int Root = 0;
LL N, X, L;
LL A, P, Q;
LL a[maxn], s[maxn];

struct Trie{
    int idx, dpvalue;
    int next[2];
}tree[NN];
int tot;

void init(int p) {
    tree[p].idx = -1; tree[p].dpvalue = -1;
    memset(tree[p].next, -1, sizeof(tree[p].next));
}

void up(int rt) {
    tree[rt].dpvalue = -1;
    int l = tree[rt].next[0], r = tree[rt].next[1];
    if(l != - 1) tree[rt].dpvalue = max(tree[rt].dpvalue, tree[l].dpvalue);
    if(r != -1) tree[rt].dpvalue = max(tree[rt].dpvalue, tree[r].dpvalue);
}

void Insert(int p, int len, LL si, int idx, int dpvalue) {
    if(len == -1) {
        //if(tree[p].dpvalue > dpvalue) return ;
        tree[p].idx = idx;
        tree[p].dpvalue = dpvalue;
        return ;
    }
    int tp = (si>>len)&1;
    if(tree[p].next[tp] == -1) {
        ++tot; init(tot);
        tree[p].next[tp] = tot;
    }
    Insert(tree[p].next[tp], len-1, si, idx, dpvalue);
    up(p);
}

void del(int p, int len, LL si, int idx) {
    if(len == -1) {   //到达叶子结点
        if(idx == tree[p].idx) {
            tree[p].idx = -1;
            tree[p].dpvalue = -1;
        }
        return ;
    }
    int tp = (si>>len)&1;
    del(tree[p].next[tp], len-1, si, idx);
    up(p);
}

int ask(int p, int len, LL si) {
    if(p == -1) return -1;        //没找到
    if(len == -1) {
        return tree[p].dpvalue;
    }
    int tpx = (X>>len)&1, tp = (si>>len)&1;
    int ans = -1;
    if(tpx == 0) {
        if(tp == 0) ans = max(ans, ask(tree[p].next[0], len-1, si));
        else ans = max(ans, ask(tree[p].next[1], len-1, si));
    }else{
        if(tp == 0) {
            if(tree[p].next[0] != -1)
                ans = max(ans, tree[tree[p].next[0]].dpvalue);
            ans = max(ans, ask(tree[p].next[1], len-1, si));
        }
        else {
            if(tree[p].next[1] != -1)
                ans = max(ans, tree[tree[p].next[1]].dpvalue);
            ans = max(ask(tree[p].next[0], len-1, si), ans);
        }
    }
    return ans;
}

int dp[maxn];

int main() {
    int T;  scanf("%d", &T);
    while(T--) {
        cin>>N>>X>>L;
        cin>>A>>P>>Q;
        a[1] = A; s[1] = A;
        for(int i=2; i<=N; i++) {
            a[i] = (a[i-1]*P + Q) % m;
            s[i] = s[i-1]^a[i];
        }
        memset(dp, 0, sizeof(dp));
        tot = 0;
        init(Root);
        int dept = 29;
        Insert(Root, dept, 0, 0, 0);
        for(int i=1; i<=N; i++) {
            int ans = ask(Root, dept, s[i]);
            if(ans >= 0) dp[i] = ans + 1;
            else dp[i] = -1;
            if(ans >= 0) Insert(Root, dept, s[i], i, dp[i]);
            if(i-L >= 0)
                del(Root, dept, s[i-L], i-L);
        }
        //printf("%d\n", dp[N]);
//        for(int i=1; i<=N; i++) printf("%d ", dp[i]);
//        printf("\n");
        if(dp[N] == -1)
            printf("0\n");
        else
            printf("%d\n", dp[N]);
    }
    return 0;
}


1012:Less Time More Profit

/*
    题意:有N个工厂, M个商店, 建设一个工厂需要花费pay单元, t时间, 建设两个以及以上的需要的时间为max(t1, t2),
    一个商店可以产生pay单元, 但是必须有k个工厂的支持, 要产生至少L个单元,问你最少花费的时间以及最多产生的单元?
    其中时间最少优先。
    tag:二分 + 最大权闭合图
    分析:我们可以二分时间然后最大化单元, 求最大单元的时候我们使用最大权闭合图即可。

*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1000;
struct edge { int to, cap, rev; };
vector<edge> tp[maxn];
vector<edge> G[maxn];               //图的邻接表示
int level[maxn];
int iter[maxn];

void add_edge(int from, int to, int cap) {
    //printf("u = %d, v = %d, cap = %d\n", from, to, cap);
    G[from].push_back((edge){to, cap, G[to].size()});
    G[to].push_back((edge){from, 0, G[from].size()-1});
}

void bfs(int s) {
    memset(level, -1, sizeof(level));
    queue<int> que;
    level[s] = 0;
    que.push(s);
    while(!que.empty()) {
        int v = que.front(); que.pop();
        for(int i=0; i<G[v].size(); i++) {
            edge &e = G[v][i];
            if(e.cap > 0 && level[e.to] < 0) {
                level[e.to] = level[v] + 1;
                que.push(e.to);
            }
        }
    }
}

int dfs(int v, int t, int f) {
    if(v == t) return f;
    for(int &i=iter[v]; i<G[v].size(); i++) {
        edge &e = G[v][i];
        if(e.cap > 0 && level[v] < level[e.to]) {
            int d = dfs(e.to, t, min(f, e.cap));
            if(d > 0) {
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s, int t) {
    int flow = 0;
    for(;;) {
        bfs(s);
        if(level[t] < 0) return flow;
        memset(iter, 0, sizeof(iter));
        int f;
        while((f=dfs(s, t, inf)) > 0)
            flow += f;
    }
}

int N, M, L;
struct Plants{
    int pay, t;
}fac[maxn];
int shop[maxn];
int s, t;
int sum_zhen;

void init() {
    for(int i=0; i<N+M+4; i++) G[i].clear();
}

int check(int mid) {
    init();
    for(int i=0; i<=N+M+2; i++) G[i] = tp[i];
    for(int i=0; i<N; i++) {
        if(fac[i].t > mid) {
            add_edge(i, t, inf);
        }else{
            add_edge(i, t, fac[i].pay);
        }
    }
    int flow = max_flow(s, t);
    return sum_zhen - flow;
}

int main() {
    int T; scanf("%d", &T);
    int kase = 0;
    while(T--) {
        scanf("%d%d%d", &N, &M, &L);   //N个工厂 M个商店
        for(int i=0; i<N; i++) {             //工厂编号  0 - N-1
            int pay, t; scanf("%d%d", &pay, &t);
            fac[i] = (Plants){pay, t};
        }
        init();
        sum_zhen = 0;
        for(int i=N; i<N+M; i++) {      //商店编号 N - N+M-1
            int pay;  scanf("%d", &pay);
            shop[i] = pay;
            sum_zhen += pay;
            int t;  scanf("%d", &t);
            while(t--) {
                int factory; scanf("%d", &factory);
                factory -= 1;
                add_edge(i, factory, inf);      //连边
            }
        }
        s = N + M;  t = N + M + 1;
        for(int i=N; i<=N+M-1; i++) {
            add_edge(s, i, shop[i]);
        }
        for(int i=0; i<=N+M+2; i++) tp[i] = G[i];

        int rest = inf, respay;
        int l = 1, r = 1000000000+100;
        while(l <= r) {
            int mid = (l+r)/2;
            int f = check(mid);
            if(f >= L) {
                if(mid < rest) rest = mid, respay = f;
                r = mid - 1;
            }else{
                l = mid + 1;
            }
        }
        if(rest == inf)
            printf("Case #%d: impossible\n", ++kase);
        else
            printf("Case #%d: %d %d\n", ++kase, rest, respay);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值