码风又变了。
BZOJ1093:
码了好久,正解挺套路的。tarjan缩点+拓扑序DP。
DP求缩完点后DAG上的最长链和最长链条数。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
const int MAXN = 100000;
using namespace std;
inline int read() {
int x = 0, w = 1;
char c = ' ';
while (c < '0' || c > '9') {
c = getchar();
if (c == '-') w = -1;
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * w;
}
struct Edge {
int to, nxt;
}edge[1000000 + 5];
int n, m, x, head[MAXN + 5], tot;
int ind, dfn[MAXN + 5], low[MAXN + 5], cnt, belong[MAXN + 5], num[MAXN + 5];
bool inSta[MAXN + 5];
stack < int > S;
vector < int > newEdge[MAXN + 5];
queue < int > Q;
int inDegree[MAXN + 5], f[MAXN + 5], maxv[MAXN + 5], pre[MAXN + 5];
inline void add(int u, int v) {
edge[tot] = (Edge) {v, head[u]};
head[u] = tot++;
}
inline void newAdd(int u, int v) {
newEdge[u].push_back(v);
++inDegree[v];
}
int tarjan(int u) {
dfn[u] = low[u] = ++ind;
S.push(u);
inSta[u] = true;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (inSta[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
int k;
++cnt;
do {
k = S.top();
belong[k] = cnt;
++num[cnt];
inSta[k] = false;
S.pop();
}while (k != u);
}
}
void topSort() {
for (int i = 1; i <= cnt; ++i)
if (!inDegree[i]) {
Q.push(i);
maxv[i] = num[i];
f[i] = 1;
}
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = 0; i < newEdge[u].size(); ++i) {
int v = newEdge[u][i];
--inDegree[v];
if (!inDegree[v]) Q.push(v);
if (pre[v] == u) continue;
if (maxv[u] + num[v] > maxv[v]) {
maxv[v] = maxv[u] + num[v];
f[v] = f[u];
}
else if (maxv[u] + num[v] == maxv[v])
f[v] = (f[v] + f[u]) % x;
pre[v] = u;
}
}
}
void init() {
memset(head, -1, sizeof(head));
tot = 0;
cnt = 0;
ind = 0;
n = read();
m = read();
x = read();
for (int i = 1; i <= m; ++i) {
int u = read(), v = read();
add(u, v);
}
}
int main() {
init();
for (int i = 1; i <= n; ++i)
if (!dfn[i]) tarjan(i);
for (int u = 1; u <= n; ++u) {
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if (belong[u] ^ belong[v]) newAdd(belong[u], belong[v]);
}
}
topSort();
int sum = 0, ans = 0;
for (int i = 1; i <= cnt; ++i)
if (ans < maxv[i]) {
ans = maxv[i];
sum = f[i];
}
else if (ans == maxv[i])
sum = (sum + f[i]) % x;
printf("%d\n%d", ans, sum);
return 0;
}
UOJ #3
用\(LCT\)动态维护最小生成树。
把边权看成点权。
怕被卡常数,所以用并查集维护连通性。
代码写起来挺无脑的。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls ch[0]
#define rs ch[1]
#define lson s[x].ls
#define rson s[x].rs
#define father s[x].fa
const int MAXN = 200000;
const int INF = 0x3f3f3f3f;
using namespace std;
inline int min(int a, int b) {
return a < b ? a : b;
}
inline int read() {
int x = 0, w = 1;
char c = ' ';
while (c < '0' || c > '9') {
c = getchar();
if (c == '-') w = -1;
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * w;
}
struct Ufs {
int fa[MAXN + 5], rk[MAXN + 5];
Ufs() {
for (int i = 1; i <= MAXN; ++i) {
fa[i] = i;
rk[i] = 1;
}
}
int find(int x) {
return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
void unionSet(int x, int y) {
int p = find(x), q = find(y);
if (p != q) {
if (rk[p] < rk[q])
fa[p] = q;
else {
if (rk[p] == rk[q]) ++rk[q];
fa[q] = p;
}
}
}
}S;
struct LinkCutTree {
struct Node {
int ch[2], fa, data, mx;
bool rev;
}s[MAXN + 5];
int st[MAXN + 5], top;
void pushUp(int x) {
int &mx = s[x].mx = x;
if (lson && s[s[lson].mx].data > s[mx].data) mx = s[lson].mx;
if (rson && s[s[rson].mx].data > s[mx].data) mx = s[rson].mx;
}
void pushDown(int x) {
if (s[x].rev) {
s[x].rev ^= 1;
s[lson].rev ^= 1;
s[rson].rev ^= 1;
swap(lson, rson);
}
}
bool isRoot(int x) {
return s[father].ls != x && s[father].rs != x;
}
void rotate(int x) {
int y = father, z = s[y].fa, l, r;
if (s[y].ls == x) l = 0; else l = 1;
r = l ^ 1;
if (!isRoot(y))
if (s[z].ls == y) s[z].ls = x; else s[z].rs = x;
father = z;
s[y].fa = x;
s[ s[x].ch[r] ].fa = y;
s[y].ch[l] = s[x].ch[r];
s[x].ch[r] = y;
pushUp(y);
pushUp(x);
}
void splay(int x) {
top = 0;
st[++top] = x;
for (int i = x; !isRoot(i); i = s[i].fa)
st[++top] = s[i].fa;
for (int i = top; i; --i)
pushDown(st[i]);
while (!isRoot(x)) {
int y = father, z = s[y].fa;
if (!isRoot(y))
if (s[y].ls == x ^ s[z].ls == y) rotate(x); else rotate(y);
rotate(x);
}
}
void access(int x) {
for (int last = 0; x; last = x, x = father) {
splay(x);
rson = last;
pushUp(x);
}
}
void makeRoot(int x) {
access(x);
splay(x);
s[x].rev ^= 1;
}
void link(int x, int y) {
makeRoot(x);
father = y;
}
void cut(int x, int y) {
makeRoot(x);
access(y);
splay(y);
if (s[y].ls == x) s[y].ls = father = 0;
}
int query(int x, int y) {
makeRoot(x);
access(y);
splay(y);
return s[y].mx;
}
}Lct;
struct Edge {
int u, v, a, b;
bool operator < (const Edge &n) const {
if (b == n.b) return a < n.a; else return b < n.b;
}
}edge[MAXN + 5];
int n, m, ans;
void init() {
n = read();
m = read();
for (int i = 1; i <= m; ++i) {
edge[i].u = read();
edge[i].v = read();
edge[i].a = read();
edge[i].b = read();
}
ans = INF;
}
int main() {
init();
sort(edge + 1, edge + 1 + m);
for (int i = 1; i <= m; ++i)
Lct.s[i + n].data = edge[i].a;
for (int i = 1; i <= m; ++i) {
int u = edge[i].u, v = edge[i].v;
bool f = true;
if (S.find(u) == S.find(v)) {
int mx = Lct.query(u, v);
if (Lct.s[mx].data > edge[i].a) {
Lct.cut(edge[mx - n].u, mx);
Lct.cut(mx, edge[mx - n].v);
}
else
f = false;
}
else
S.unionSet(u, v);
if (f) {
Lct.link(u, i + n);
Lct.link(i + n, v);
}
if (S.find(1) == S.find(n)) {
int mx = Lct.query(1, n);
ans = min(ans, Lct.s[mx].data + edge[i].b);
}
}
printf("%d\n", ans == INF ? -1 : ans);
return 0;
}
BZOJ2152
点分治模板题,没什么难度。
#include <iostream>
#include <cstdio>
#include <cstring>
#define max(a, b) (a < b ? b : a)
const int MAXN = 40000;
using namespace std;
inline int read() {
int x = 0, w = 1;
char c = ' ';
while (c < '0' || c > '9') {
c = getchar();
if (c == '-') w = -1;
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * w;
}
struct Edge {
int to, w, nxt;
}edge[MAXN + 5];
int head[MAXN + 5], tot;
bool vis[MAXN + 5];
int size[MAXN + 5], depth[MAXN + 5], maxSon[MAXN + 5], sz[MAXN + 5], num[MAXN + 5], total, rt;
int ans;
void add(int u, int v, int w) {
edge[tot] = (Edge) {v, w, head[u]};
head[u] = tot++;
edge[tot] = (Edge) {u, w, head[v]};
head[v] = tot++;
}
void getCentre(int u, int fa) {
sz[u] = 1;
maxSon[u] = 0;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to, w = edge[i].w;
if (v != fa && !vis[v]) {
getCentre(v, u);
sz[u] += sz[v];
maxSon[u] = max(maxSon[u], sz[v]);
}
}
maxSon[u] = max(maxSon[u], total - sz[u]);
if (maxSon[u] < maxSon[rt]) rt = u;
}
void getDepths(int u, int fa) {
++num[depth[u]];
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to, w = edge[i].w;
if (v != fa && !vis[v]) {
depth[v] = (depth[u] + w) % 3;
getDepths(v, u);
}
}
}
int calc(int u, int w) {
num[0] = num[1] = num[2] = 0;
depth[u] = w;
getDepths(u, -1);
return num[1] * num[2] * 2 + num[0] * num[0];
}
void divideAndConquer(int u) {
ans += calc(u, 0);
vis[u] = true;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to, w = edge[i].w;
if (!vis[v]) {
ans -= calc(v, w);
rt = 0;
total = sz[v];
getCentre(v, -1);
divideAndConquer(rt);
}
}
}
int n;
void init() {
memset(head, -1, sizeof(head));
n = read();
for (int i = 1; i < n; ++i) {
int u = read(), v = read(), w = read();
add(u, v, w % 3);
}
total = n;
rt = ans = 0;
maxSon[rt] = n;
getCentre(1, -1);
}
inline int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int main() {
init();
divideAndConquer(rt);
int tmp = gcd(ans, n * n);
printf("%d/%d\n", ans / tmp, n * n / tmp);
return 0;
}
洛谷P2365
可以单调队列优化,懒得写了。
#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a, b) (a < b ? a : b)
const int MAXN = 5000;
using namespace std;
inline int read() {
int x = 0, w = 1;
char c = ' ';
while (c < '0' || c > '9') {
c = getchar();
if (c == '-') w = -1;
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * w;
}
int f[MAXN + 5], t[MAXN + 5], dp[MAXN + 5];
int n, s;
void init() {
memset(dp, 0x3f, sizeof(dp));
n = read();
s = read();
for (int i = 1; i <= n; ++i) {
t[i] = read() + t[i - 1];
f[i] = read() + f[i - 1];
}
}
int main() {
init();
dp[0] = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j)
dp[i] = min(dp[i], dp[j - 1] + t[i] * (f[i] - f[j - 1]) + s * (f[n] - f[j - 1]));
printf("%d\n", dp[n]);
return 0;
}
POJ1180:
上一题的单调队列优化版。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a, b) (a < b ? a : b)
const int MAXN = 10000;
using namespace std;
inline int read() {
int x = 0, w = 1;
char c = ' ';
while (c < '0' || c > '9') {
c = getchar();
if (c == '-') w = -1;
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * w;
}
int c[MAXN + 5], t[MAXN + 5], dp[MAXN + 5];
int n, s;
int f, r, que[MAXN + 5];
void init() {
memset(dp, 0x3f, sizeof(dp));
n = read();
s = read();
for (int i = 1; i <= n; ++i) {
t[i] = read() + t[i - 1];
c[i] = read() + c[i - 1];
}
f = 0;
r = 0;
que[f] = 0;
}
int main() {
init();
dp[0] = 0;
for (int i = 1; i <= n; ++i) {
while (f < r && dp[que[f + 1]] - dp[que[f]] <= (s + t[i]) * (c[que[f + 1]] - c[que[f]])) ++f;
int k = que[f];
dp[i] = min(dp[i], dp[k] + t[i] * (c[i] - c[k]) + s * (c[n] - c[k]));
while (f < r && (dp[i] - dp[que[r]]) * (c[que[r]] - c[que[r - 1]]) <= (dp[que[r]] - dp[que[r - 1]]) * (c[i] - c[que[r]])) --r;
que[++r] = i;
}
printf("%d\n", dp[n]);
return 0;
}