近日为与Fractal128增进感情而做的一些努力(一)

老是把老的文章发上来好像不是很够意思。其实最近一直都有在做题,在做Fractal128博客的题。本着在退役之前至少能和Fractal聊到一块去的目标,刷了好多他的题,许多题是一边做一边骂的,因为他的题解有时也太偷工减料了。这篇文章我记录一下最近一个月做过的题。凭我这记性看看旧题效果要比做新的好吧。

APIO之后,OI生涯估计就告一段落了。

如果算法真的是我喜欢的东西的话,相信以后也会继续下去。

网络流、二分图之类

这个其实好烧脑。。。(mdzz

[网络流24题]魔术球问题(简化版)

考察点:给出一个DAG,求出其最小路径覆盖(不可重复走的)

啥叫最小路径覆盖?就是用最少的路径条数就把整张DAG的点全部走一遍。
回顾一下二分图的一个基本常识: 最大匹配 = 最小顶点覆盖
那啥,什么事最小点覆盖?//mdzz
二分图最小点覆盖的定义:
二分图中,选取最少的点数,使这些点和所有的边都有关联(把所有的边的覆盖),叫做最小点覆盖。
我们把DAG的每个点拆成两个,把出边和入边分开。
这样我们就搞出了一张二分图对吧。
我们观察一下这张二分图的最小顶点覆盖的特点。
最小顶点覆盖把所有的边都覆盖了。
剩下的点都是路径的终点。//并没有证明(可能是错的
所以最后可以得出:最小路径覆盖数 = 总点数 - 最小顶点覆盖数

BZOJ1143: [CTSC2008]祭祀river

给出一个DAG,求出其最大点独立集(集合间任意两点不联通)

其实我并不会证明。。。

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define filenamein "1.in"
#define filenameout "1.out"
#define maxn 110
typedef long long ll;
using namespace std;
int read(){
int x = 0, f = 1; char ch = getchar();
while (ch > '9' || ch < '0'){ if (ch == '-') f = -1; ch = getchar();}
while (ch <= '9' && ch >= '0'){ x = x * 10 + ch -'0'; ch = getchar();}
return x*f;
}
int n, m;
bool f[maxn][maxn];
int p[maxn], vis[maxn];
int ind;
bool find(int u){
for (int v = 1; v <= n; v++){
if (vis[v]!=ind && f[u][v]){
vis[v] = ind;
if (!p[v] || find(p[v])){
p[v] = u;
return true;
}
}
}
return false;
}
int hungary(){
int ret = 0;
ind = 1;
for (int i = 1; i <= n; i++, ind++)
if (find(i)) ret++;
return ret;
}
int main(){
freopen(filenamein, "r", stdin);
freopen(filenameout, "w", stdout);
n = read(); m = read();
memset(f, 0, sizeof f);
for (int i = 0; i < m; i++){
int x = read(), y = read();
f[x][y] = true;
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
f[i][j] |= f[i][k] & f[k][j];
int num = hungary();
printf("%d\n", n - num);
return 0;
}

BZOJ2127:happiness

神奇的构图。。

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#define filenamein "1.in"
#define filenameout "1.out"
#define p(i,j) (i-1)*m+j
#define S 0
#define T 10010
#define maxn 110
#define INF 1000000000
typedef long long ll;
using namespace std;
int read(){
int x = 0, f = 1; char ch = getchar();
while (ch > '9' || ch < '0'){ if (ch == '-') f = -1; ch = getchar();}
while (ch <= '9' && ch >= '0'){ x = x * 10 + ch -'0'; ch = getchar();}
return x*f;
}
int n, m, w[maxn][maxn], l[maxn][maxn], a[maxn][maxn], b[maxn][maxn], c[maxn][maxn], d[maxn][maxn], e;
struct ed
{
int f, u, v, l, nex;
}ed[maxn*maxn*6];
int fir[maxn*maxn];
void addedge(int u, int v, int w, int l){
ed[++e].u = u, ed[e].v = v, ed[e].f = w, ed[e].l = e + l;
ed[e].nex = fir[u];
fir[u] = e;
}
queue <int> q;
int dis[maxn*maxn];
bool bfs(){
memset(dis, -1, sizeof dis);
q.push(S); dis[S] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
for (int i = fir[u]; i; i = ed[i].nex){
int v = ed[i].v;
if (ed[i].f && dis[v] == -1){
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
if (dis[T] != -1) return true;
return false;
}
int find(int u, int flw = INF){
if (u == T) return flw;
int flow = 0;
for (int i = fir[u]; i; i = ed[i].nex){
int v = ed[i].v;
if (ed[i].f && dis[v] == dis[u] + 1 && (flow = find(v, min(ed[i].f, flw)))){
ed[i].f -= flow;
ed[ed[i].l].f += flow;
return flow;
}
}
return 0;
}
int dinic(){
int ret = 0, flow = 0;
while (bfs()){
//printf("%d\n", dis[T]);
while (flow = find(S))
ret += flow;
}
return ret;
}

int main(){
freopen(filenamein, "r", stdin);
freopen(filenameout, "w", stdout);
n = read(); m = read();
int tot = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
w[i][j] = read(), tot += w[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
l[i][j] = read(), tot += l[i][j];
for (int i = 1; i <= n - 1; i++)
for (int j = 1; j <= m; j++)
a[i][j] = read(), tot += a[i][j];
for (int i = 1; i <= n - 1; i++)
for (int j = 1; j <= m; j++)
b[i][j] = read(), tot += b[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m - 1; j++)
c[i][j] = read(), tot += c[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m - 1; j++)
d[i][j] = read(), tot += d[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++){
if (i != n) {
int W = (a[i][j] + b[i][j]) ;
addedge(p(i, j), p(i + 1, j), W, 1);
addedge(p(i + 1, j), p(i, j), W, -1);
}
if (j != m){
int W = (c[i][j] + d[i][j]) ;
addedge(p(i, j), p(i, j + 1), W, 1);
addedge(p(i, j + 1), p(i, j), W, -1);
}
int W = 2 * w[i][j] + (a[i][j] + c[i][j] + a[i - 1][j] + c[i][j - 1]) ;
addedge(S, p(i, j), W, 1);
addedge(p(i, j), S, 0, -1);
W = 2 * l[i][j] + (b[i][j] + d[i][j] + b[i - 1][j] + d[i][j - 1]) ;
addedge(p(i, j), T, W, 1);
addedge(T, p(i, j), 0, -1);
}
int x = dinic();
printf("%d\n", tot - x / 2);
return 0;
}

数学、数论、群论之类

BZOJ1004:[HNOI2008]Cards

置换及其应用:
Burnside引理:对于一个置换f,若一个着色方案s经过置换后不变,称s为f的不动点。将f的不动点数目记为C(f),则可以证明等价类问题为所有C(f)的平均值。
怎么求C(f)?
如果把置换f分解成m(f)个循环额乘积,那么每个循环内所有状态必须相同,假设有K种状态,则$C(f)=k^{m(f)}$
Polya定理:等价类的个数等于所有置换f的$k^{m(f)}$的平均数。

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define filenamein "1.in"
#define filenameout "1.out"
#define maxn 100
#define maxa 30
typedef long long ll;
using namespace std;

int n, sr, sb, sg, m, p;
int ans = 0;
int f[maxn][maxa][maxa];
int s[maxn], Sum_s[maxn];
bool vis[maxn];
int a[maxn];

int read(){
int x = 0, f = 1; char ch = getchar();
while (ch > '9' || ch < '0'){ if (ch == '-') f = -1; ch = getchar();}
while (ch <= '9' && ch >= '0'){ x = x * 10 + ch -'0'; ch = getchar();}
return x*f;
}
int dfs(int i){
int sum = 1; vis[i] = true;
if (!vis[a[i]]) sum += dfs(a[i]);
return sum;
}
int exgcd(int a, int b, int& x, int& y){
if (b == 0){
x = 1; y = 0;
return a;
}
exgcd(b, a % b, x, y);
int tmp = x;
x = y;
y = tmp - (a / b) * y;
}

int main(){
freopen(filenamein, "r", stdin);
freopen(filenameout, "w", stdout);
sr = read(); sb = read(); sg = read(); m = read(); p = read();
n = sr + sb + sg;
for (int t = 1; t <= m + 1; t++){
if (t != m + 1){
for (int i = 1; i <= n; i++){
a[i] = read();
}
} else for (int i = 1; i <= n; i++) a[i] = i;
memset(vis, 0, sizeof vis);
memset(Sum_s, 0, sizeof Sum_s);
memset(s, 0, sizeof s);
int num = 0;

for (int i = 1; i <= n; i++)if (!vis[i]) {
int sum = dfs(i);
s[++num] = sum;
Sum_s[num] = Sum_s[num - 1] + s[i];
}
memset(f, 0, sizeof f);
f[0][0][0] = 1;
for (int i = 1; i <= num; i++)
for (int r = 0; r <= sr; r++)
for (int b = 0; b <= sb; b++){
int g = Sum_s[i] - r - b;
if (g < 0) continue;
int tmp = 0;
if (r >= s[i]) tmp = (tmp + f[i-1][r-s[i]][b]) % p;
if (b >= s[i]) tmp = (tmp + f[i-1][r][b-s[i]]) % p;
if (g >= s[i]) tmp = (tmp + f[i-1][r][b]) % p;
f[i][r][b] = tmp;
}
ans = (ans + f[num][sr][sb]) % p;
}
int x, y;
exgcd(m + 1, p, x, y);
printf("%d\n", (x * ans % p + p) % p);
return 0;
}

BZOJ2882: 工艺

最小表示法

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define filenamein "1.in"
#define filenameout "1.out"
typedef long long ll;
using namespace std;
int a[500000], n;
int read(){
int x = 0, f = 1; char ch = getchar();
while (ch > '9' || ch < '0'){ if (ch == '-') f = -1; ch = getchar();}
while (ch <= '9' && ch >= '0'){ x = x * 10 + ch -'0'; ch = getchar();}
return x*f;
}
int minR(){
int i = 0, j = 1, k = 0;
while (i < n && j < n && k < n){
int t = a[(i + k < n) ? i + k : i + k - n] - a[(j + k < n) ? j + k : j + k - n];
if (!t) k++; else {
if (t > 0) i = i + k + 1; else j = j + k + 1;
if (i == j) j++;
k = 0;
}
}
return min(i, j);
}
int main(){
freopen(filenamein, "r", stdin);
freopen(filenameout, "w", stdout);
n = read();
for (int i = 0; i < n; i++) a[i] = read();
int x = minR();
for (int i = 0; i < n - 1; i++)
printf("%d ", a[(x+i<n)?x+i:x+i-n]);
printf("%d", a[(x+n-1<n)?x+n-1:x+n-1-n]);

return 0;
}

还有一道vijos的1382

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define filenamein "1.in"
#define filenameout "1.out"
typedef long long ll;
using namespace std;
int l = 0;
char a[1000010], b[1000010];
int numR(char* a){
int i = 0, j = 1, k = 0;
while (i < l && j < l && k < l){
int t = a[(i + k) % l] - a[(j + k) % l];
if (!t) k++; else {
if (t > 0) i = i + k + 1; else j = j + k + 1;
if (i == j) j++;
k = 0;
}
}
return min(i, j);
}
int main(){
freopen(filenamein, "r", stdin);
freopen(filenameout, "w", stdout);
scanf("%s", a);
scanf("%s", b);
l = strlen(a);
int x = numR(a), y = numR(b);
bool bo = true;
for (int i = 0; i < l; i++)
if (a[(i + x) % l] != b[(i + y) % l]) {
bo = false;
break;
}
if (bo) {
printf("Yes\n");
for (int i = 0; i < l; i++)
printf("%c", a[(i + x) % l]);
} else printf("No\n");
return 0;
}

BZOJ2190: [SDOI2008]仪仗队

线性筛计算欧拉函数

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define filenamein "1.in"
#define filenameout "1.out"
typedef long long ll;
using namespace std;
bool vis[40010];
int phi[40010], p[40010], tot = 0, n;
void get_phi(int n){
for (int i = 2; i <= n; i++){
if (vis[i] == 0){
vis[i] = true; p[++tot] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= tot; j++){
if (i * p[j] > n) break;
vis[i * p[j]] = true;
if (i % p[j] == 0) {
phi[i * p[j]] = phi[i] * p[j];
break;
} else
phi[i * p[j]] = phi[i] * (p[j] - 1);
}
}
}

int main(){
freopen(filenamein, "r", stdin);
freopen(filenameout, "w", stdout);
scanf("%d", &n);
get_phi(n);
ll ans = 0;
for (int i = 2; i < n; i++) ans += (ll) phi[i];
ans *= 2;
ans += 3;
printf("%d\n", ans);
return 0;
}

BZOJ2705: [SDOI2012]Longge的问题

欧拉函数O(sqrt(n))

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define filenamein "1.in"
#define filenameout "1.out"
typedef long long ll;
using namespace std;
ll read(){
ll x = 0, f = 1; char ch = getchar();
while (ch > '9' || ch < '0'){ if (ch == '-') f = -1; ch = getchar();}
while (ch <= '9' && ch >= '0'){ x = x * 10 + ch -'0'; ch = getchar();}
return x*f;
}
ll n, ans = 0;
ll phi(ll x){
ll t = x, ret = x;
for (ll i = 2; i * i <= x; i++)
if (!(t % i)) {
ret = ret / i * (i - 1);
while (!(t % i)) t /= i;
}
if (t > 1) ret = ret / t * (t - 1);
return ret;
}
int main(){
freopen(filenamein, "r", stdin);
freopen(filenameout, "w", stdout);
n = read();
for (ll i = 2; i * i <= n; i++){
if (!(n % i)){
ans += n / i * phi(i);
if (i * i != n) ans += i * phi(n / i);
}
}
printf("%lld\n", ans+n+phi(n));
return 0;
}

BZOJ3043: IncDec Sequence

有趣的差分题(没写代码

未完待续