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