http://acm.hdu.edu.cn/contests/contest_show.php?cid=880

1001 - Total Eclipse

 

题意:给定一张无向图,每点有点权,每次操作选一个联通子图,每点点权-1,问最少多少次操作后所有点点权都为 0 .

并查集+贪心

贪心地每次选择最大地联通子图,所有点权-1,直到有些点权为 0,再选择分裂开的新的联通子图,直到所有点权都为 0.

这样就要分治,每次找到分裂点,时间不够。所以点权减少行不通就要逆向考虑。

考虑在全是 0 的图上加点权。

按照点权从大到小排序,点权最大的先放进图里,以后每次操作一定都会选它。

加入一个新点时,要把新图上的所有点的点权都+1,由于每个联通块算作一次操作,所以操作数就是加上图上加入新点后的联通块个数。

由于点权可能不是连续的,所以每加入一个点,操作数还要乘上新点权和上一个点权的差值。

用并查集维护联通块合并,同时为了动态记录联通块个数,还要一个set,每次旧点向新点合并,并把旧点删掉,加入新点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
const ll mod = 1000000007;
int par[N];
vector<int>G[N];
int n, m;
int b[N], vis[N];
set<int>st;
void init(int n) { for (int i = 0; i <= n; i++)par[i] = i, G[i].clear(); }
int find(int x) { return par[x] == x ? x : par[x] = find(par[x]); }
void uni(int x, int y) { par[find(x)] = find(y); }
int T;
typedef pair<int, int>pii;
vector<pii>vc;
int main(){
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
init(n);
st.clear();
for (int i = 1; i <= n; i++)scanf("%d", &b[i]), vis[i] = 0;
for (int i = 0; i < m; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
ll ans = 0ll;
vc.clear();
for (int i = 1; i <= n; i++)vc.push_back(pii(b[i], i));
sort(vc.begin(), vc.end(), greater<pii>());
vis[vc[0].second] = 1;
st.insert(vc[0].second);
for (int i = 1; i < (int)vc.size(); i++) {
ans += 1ll * (vc[i - 1].first - vc[i].first)*(int)st.size();
st.insert(vc[i].second);
int u = vc[i].second;
for (int v : G[u]) {
if (vis[v]) {
if (find(v) == find(u))continue;
st.erase(find(v));
uni(v, u);
}
}
vis[u] = 1;
}
ans += 1ll * vc.back().first*(int)st.size();
printf("%lld\n", ans);
}
return 0;
}

 

1010 - Led of Wisdom

 

题意:给定 n 个装备,每个装备有 a,b,c,d 四种属性值以及它所属的类型,每种类型最多选一个,要求最大化 DMG=(100+ai)(100+bi)(100+ci)(100+di)DMG=(100+∑ai)(100+∑bi)(100+∑ci)(100+∑di)

数据很小,肯定是dfs爆搜。

但是由于可能有些类型没有武器,所以如果遍历所有类型的话,复杂度可能会退化。

因此必须先预处理,跳过所有为空的类型,注意这里的跳过并不是return,而是预处理时处理出每种类型后一个非空的类型,dfs时按照这个链表逐层递归。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
const ll mod = 1000000007;
int T;
int n, k;
ll A, B, C, D;
ll ans;
int a[N], b[N], c[N], d[N], cnt[N];
int p[100][100], nxt[N];
void dfs(int dep) {
if (dep == k + 1) {
ans = max(ans, 1ll * (100 + A)*(100 + B)*(100 + C)*(100 + D));
return;
}
for (int i = 1; i <= cnt[dep]; i++) {
int u = p[dep][i];
A += a[u];
B += b[u];
C += c[u];
D += d[u];
dfs(nxt[dep]);
A -= a[u];
B -= b[u];
C -= c[u];
D -= d[u];
}
}
int main() {
scanf("%d", &T);
while (T--) {
ans = 0;
A = B = C = D = 0;
scanf("%d%d", &n, &k);
for (int i = 1; i <= k; i++)cnt[i] = 0;
for (int i = 1; i <= n; i++) {
int t;
scanf("%d%d%d%d%d", &t, &a[i], &b[i], &c[i], &d[i]);
p[t][++cnt[t]] = i;
}
nxt[k] = k + 1;
for (int i = k - 1; i >= 0; i--) {
if (cnt[i + 1] > 0)nxt[i] = i + 1;
else nxt[i] = nxt[i + 1];
}
dfs(nxt[0]);
printf("%lld\n", ans);
}
return 0;
}

 

1006 - The Oculus

 

题意:给定 A,B 的斐波那契编码,,给出的 C 的斐波那契编码,是 A*B 的斐波那契编码第 k 位由 1 变为 0。问 k。

ABC=fkAB-C=f_k ,如果能找到一个模数,使得所有斐波那契数模它的余数都不同,则可以再遍历确定 k。

多试几个或者 2642^{64} 自然溢出就是一个满足的模数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 10;
const ll mod = 1000000009;
int T;
int a[N], b[N], c[N];
int A, B, C;
int f[N];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &A);
for (int i = 1; i <= A; i++)scanf("%d", &a[i]);
scanf("%d", &B);
for (int i = 1; i <= B; i++)scanf("%d", &b[i]);
scanf("%d", &C);
for (int i = 1; i <= C; i++)scanf("%d", &c[i]);
f[1] = 1; f[2] = 2;
for (int i = 3; i <= C; i++) {
f[i] = (f[i - 1] + f[i - 2]) % mod;
}
ll x = 0ll;
for (int i = 1; i <= A; i++) {
if (a[i])x = (x + f[i]) % mod;
}
ll y = 0ll;
for (int i = 1; i <= B; i++) {
if (b[i])y = (y + f[i]) % mod;
}
ll z = 0ll;
for (int i = 1; i <= C; i++) {
if (c[i])z = (z + f[i]) % mod;
}
ll t = (x*y%mod - z + mod) % mod;
for (int i = 1; i <= C; i++) {
if (!c[i] && !c[i - 1] && !c[i + 1] && f[i] == t) {
printf("%d\n", i);
break;
}
}
}
return 0;
}

 

1005 - New Equipments

 

题意:给定 n 个工人,1到m,共 m 份工作,每个工人有a,b,c属性值,第 i 个工人选第 j 个工作的代价是 a[i]j2+b[i]j+c[i]a[i]*j^2+b[i]*j+c[i],每份工作只能有一个工人,每个工人必须选一份工作。问当有 k 个工人满足条件时,最小代价。

费用流

每个工人是一个二次函数,并且可以得到它的对称轴。

对于每个工人,找到对称轴附近 n 个函数值最小的点,与对应的工作连接,费用为函数值。

所有边容量都为 1。除工人连向工作的边之外的边费用都为 0。

求一个最小权完美匹配。

Hall定理:设二分图中G=<V1,V2,E>中 |V1|=m<=|V2|=n,G中存在从V1到V2的完全匹配当且仅当V1中任意k(k=1,2,…,m)个顶点至少与V2中k个顶点是相邻的。

每个点与 n 个不同的点连接,所以每 k 个点都必定与至少 n 个不同的点连接,由Hall定理可知,必存在完美匹配。

每条增广路径容量都是 1,所以增广 n 次,并记录下每次的费用,最终输出。

unique之前要排序!!怎么又忘了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
const ll mod = 1000000007;
int T;
struct E
{
int from, to, cp;
ll v;
int rev;
E() {}
E(int f, int t, int cp, ll v, int rev) :from(f), to(t), cp(cp), v(v), rev(rev) {}
};
typedef pair<ll, ll>pii;
ll ans[100];
struct MCMF
{
int n, m, s, t;
vector<E> edges;
vector<int> G[N];
bool inq[N]; //是否在队列
ll d[N]; //Bellman_ford单源最短路径
int p[N]; //p[i]表从s到i的最小费用路径上的最后一条弧编号
int a[N]; //a[i]表示从s到i的最小残量
void init(int _n, int _s, int _t) {
n = _n; s = _s; t = _t;
for (int i = 0; i < n; i++) G[i].clear();
edges.clear(); m = 0;
}

void addedge(int from, int to, int cap, ll cost) {
edges.push_back(E(from, to, cap, cost, 0));
edges.push_back(E(to, from, 0, -cost, 1));
G[from].push_back(m++);
G[to].push_back(m++);
}

bool BellmanFord(int &flow, ll &cost) {
for (int i = 0; i < n; i++) d[i] = inf;
memset(inq, 0, sizeof inq);
d[s] = 0, a[s] = INF, inq[s] = true;
queue<int> Q; Q.push(s);
while (!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = false;
for (int& idx : G[u]) {
E &e = edges[idx];
if (e.cp && d[e.to] > d[u] + e.v) {
d[e.to] = d[u] + e.v;
p[e.to] = idx;
a[e.to] = min(a[u], e.cp);
if (!inq[e.to]) {
Q.push(e.to);
inq[e.to] = true;
}
}
}
}
if (d[t] == inf) return false;
flow += a[t];
cost += a[t] * d[t];
int u = t;
while (u != s) {
edges[p[u]].cp -= a[t];
edges[p[u] ^ 1].cp += a[t];
u = edges[p[u]].from;
}
return true;
}

pii go(int t) {
int flow = 0; ll cost = 0ll;
for (int i = 1; i <= t; i++) {
BellmanFord(flow, cost);
ans[i] = cost;
}
return pii(flow, cost);
}
} MM;
int n; ll m;
ll a[100], b[100], c[100];
vector<pii>vc[100];
vector<ll>vv;
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%lld", &n, &m);
for (int i = 1; i <= n; i++)scanf("%lld%lld%lld", &a[i], &b[i], &c[i]);
vv.clear();
for (int i = 1; i <= n; i++) {
vc[i].clear();
ll x = -b[i] / (2 * a[i]);
if (x <= 1) {
for (int j = 1; j <= n; j++)vc[i].push_back(pii(a[i] * j*j + b[i] * j + c[i], j));
}
else if (x >= m) {
for (ll j = m; j >= m - n + 1; j--) {
vc[i].push_back(pii(a[i] * j*j + b[i] * j + c[i], j));
}
}
else {
for (ll j = x; j >= max(1ll, x - n + 1); j--) {
vc[i].push_back(pii(a[i] * j*j + b[i] * j + c[i], j));
}
for (ll j = x + 1; j <= min(m, x + n); j++) {
vc[i].push_back(pii(a[i] * j*j + b[i] * j + c[i], j));
}
}
sort(vc[i].begin(), vc[i].end());
for (int j = 0; j < n; j++) {
vv.push_back(vc[i][j].second);
}
}
sort(vv.begin(), vv.end());
vv.erase(unique(vv.begin(), vv.end()), vv.end());
int S = 0, T = n + (int)vv.size() + 1;
MM.init(T + 1, S, T);
for (int i = 1; i <= n; i++) {
MM.addedge(S, i, 1, 0);
for (int j = 0; j < n; j++) {
pii p = vc[i][j];
int t = lower_bound(vv.begin(), vv.end(), p.second) - vv.begin() + 1;
MM.addedge(i, n + t, 1, p.first);
}
}
for (int i = 1; i <= (int)vv.size(); i++) {
MM.addedge(n + i, T, 1, 0);
}
MM.go(n);
for (int i = 1; i <= n; i++)printf("%lld%s", ans[i], i == n ? "\n" : " ");
}
return 0;
}