存题处
/******************/
为了迎接社会主义现代化建设所必须要的技术:
1、尺取法
2、反转法
3、弹性碰撞
4、折半枚举
5、坐标离散化
一、动规
1.概率dp
URAL 1776
Anniversary Firework
思路:此题直接求期望太过复杂。因为期望等于所有可能答案的概率与该答案的值的乘积的累加和,所以,我们可以求对于每个可能答案的概率,由此简化问题。
2 #include <algorithm>
3 #include <vector>
4 #include <cstring>
5
6 const int N = 400 + 10 ;
7 double d[N][N], sum[N][N];
8
9 int main() {
10 int n;
11 scanf( " %d " , &n);
12 n -= 2 ;
13
14 memset(d, 0 , sizeof d);
15 memset(sum, 0 , sizeof sum);
16
17 d[ 0 ][ 0 ] = 1 .;
18 for ( int j = 0 ; j <= n; ++j)
19 sum[ 0 ][j] = 1 .;
20
21 for ( int i = 1 ; i <= n; ++i) {
22 for ( int j = 0 ; j <= i - 1 ; ++j) {
23 int l = j, r = i - 1 - j;
24 int t = std::max(l, r) + 1 ;
25
26 for ( int k = 1 ; k <= t; ++k) {
27 d[i][k] += (d[l][k - 1 ] * sum[r][k - 1 ] + d[r][k - 1 ] * sum[l][k - 1 ] - d[l][k - 1 ] * d[r][k - 1 ]) / i;
28 }
29
30 for ( int j = 1 ; j <= n; ++j)
31 sum[i][j] = sum[i][j - 1 ] + d[i][j];
32 }
33 }
34
35 double ans = 0 ;
36 for ( int j = 1 ; j <= n; ++j)
37 ans = ans + d[n][j] * j * 10 ;
38
39 printf( " %.8f\n " , ans);
40 return 0 ;
41 }
2.树形dp
题意:n张牌,如果第i张牌与第i-1或者第i-3张牌花色一样或者数字一样,那么可以将这两张牌合并,合并后,第i张牌覆盖合并的牌。
问将n张牌合并成一张牌是否可能。n <=52。
思路:dp(i,a,b,c)//前i张牌,第i张是c,第i-1张是b,第i-2张是c时是否可以合并成功
3.朴素dp
二、图论
1.最短路
2.网络流
URAL 1774 Barber of the Army of Mages三、数据结构
1.并查集
2.树状数组
UVA 12429
Finding Magic Triplets
题意:对于给定的n和K,要求满足a+b*b=c*c*c(mod K) ——(1<=a<=b<=c<=n)的组合数
方法:我们发现,当我们固定b的时候,左侧是一个连续的区间,我们可以预先将右侧的值存入树状数组,然后询问区间和即可。
要注意把握连续区间这一特点,还有固定中间值这个方法
2 #include <cstring>
3 #include <algorithm>
4 typedef long long ll;
5 #define lson l, mid, rt << 1
6 #define rson mid + 1, r, rt << 1 | 1
7 const int N = 100000 + 10 ;
8 int T = 0 ;
9
10 ll sum[N];
11 int K;
12
13 void add( int pos, ll v) {
14 if (pos == 0 ) sum[ 0 ] += v;
15 else {
16 for (;pos < K; pos += pos & -pos)
17 sum[pos] += v;
18 }
19 }
20 ll query( int pos) {
21 if (pos < 0 ) return 0 ;
22 else {
23 ll re = sum[ 0 ];
24 for (;pos > 0 ; pos -= pos & -pos)
25 re += sum[pos];
26 return re;
27 }
28 }
29 inline ll Q( int L, int R) {
30 return query(R) - query(L - 1 );
31 }
32 void work() {
33 int n;
34 memset(sum, 0 , sizeof (ll) * K);
35 scanf( " %d%d " , &n, &K);
36
37 int l, r, v;
38 ll ans = 0 ;
39 for ( int b = n; b >= 1 ; --b) {
40 v = (ll)b * b * b % K;
41 add(v, 1 );
42
43 v = (ll)b * b % K;
44 if (K > b) {
45 l = 1 ; r = b;
46 l = (l + v) % K; r = (r + v) % K;
47 if (r < l) {
48 ans += Q( 0 , r) + Q(l, K - 1 );
49 } else {
50 ans += Q(l, r);
51 }
52 } else {
53 l = 0 ; r = K - 1 ;
54 l = (l + v) % K; r = (r + v) % K;
55 if (r < l) {
56 ans += Q( 0 , r) * (b / K) + Q(l, K - 1 ) * (b / K);
57 } else {
58 ans += Q(l, r) * (b / K);
59 }
60 if (b % K > 0 ) {
61 l = 1 ; r = b % K;
62 l = (l + v) % K; r = (r + v) % K;
63 if (r < l) {
64 ans += Q( 0 , r) + Q(l, K - 1 );
65 } else {
66 ans += Q(l, r);
67 }
68 }
69 }
70 }
71
72 printf( " Case %d: %lld\n " , ++ T, ans);
73 }
74 int main() {
75 int cas;
76 scanf( " %d " , &cas);
77 while (cas -- > 0 ) {
78 work();
79 }
80 return 0 ;
81 }
题意:给定n个pair型,对于每一对pair型,ans+=max(a.first,b.first) * abs(a.second - b.second),求最后的结果。
思路:按照first排序,然后树状数组维护
2 #include <cstring>
3 #include <algorithm>
4 const int N = 200000 + 10 ;
5 typedef long long ll;
6 struct node {
7 int pos;
8 ll v;
9 };
10
11 node ar[N];
12
13 struct Bit {
14 ll sum[N];
15 void clear() {
16 memset(sum, 0 , sizeof sum);
17 }
18 void add( int pos, ll v) {
19 if (pos == 0 ) sum[ 0 ] += v;
20 else {
21 for (;pos < N; pos += pos & -pos)
22 sum[pos] += v;
23 }
24 }
25 ll query( int pos) {
26 if (pos < 0 ) return 0 ;
27 else {
28 ll re = sum[ 0 ];
29 for (;pos > 0 ; pos -= pos & -pos)
30 re += sum[pos];
31 return re;
32 }
33 }
34 };
35
36 Bit a, b;
37 int n;
38
39 bool cmp( const node& a, const node& b) {
40 return a.v < b.v;
41 }
42 void work() {
43 a.clear(); b.clear();
44
45 for ( int i = 0 ; i < n; ++i)
46 scanf( " %d%d " , &ar[i].v, &ar[i].pos);
47
48 std::sort(ar, ar + n, cmp);
49
50 ll ans = 0 , tot = 0 ;
51 for ( int i = 0 ; i < n; ++i) {
52 ll w = a.query(ar[i].pos);
53 ll n1 = b.query(ar[i].pos);
54
55 ans += (n1 * ar[i].pos - w) * ar[i].v + (tot - w - (i - n1) * ar[i].pos) * ar[i].v;
56 // printf("%lld\n", ans);
57 a.add(ar[i].pos, ar[i].pos);
58 b.add(ar[i].pos, 1 );
59
60 tot = tot + ar[i].pos;
61 }
62
63 printf( " %lld\n " , ans);
64 }
65 int main() {
66 while ( 1 == scanf( " %d " , &n)) {
67 work();
68 }
69 return 0 ;
70 }
poj
3109
Inner Vertices
题意:给100000个黑色点,如果两对点的连线相交于一个白色点,则此点变成黑色。问最终会有多少个点。
思路:扫描线。
预处理:1,离散化坐标 2,找出所有最长垂直线段和水平线段 3,垂直线段按照x轴坐标分类 4,水平线段按照起点x轴坐标分类(3,4的线段放入vector中)
核心:1,枚举所有横坐标x,将所有起点坐标>=x的水平线段加入(在树状数组中更新其y坐标) 2,将所有终点坐标<x的水平线段删除(用优先队列维护,队首是终点最小;同时更新树状数组) 3,此时树状数组中所有的y值对应的水平线段都是横跨垂直线段x的,更新答案。
2 #include <cstring>
3 #include <algorithm>
4 #include <vector>
5 #include <queue>
6 typedef std::pair< int, int> pii;
7 const int INF = ( int)(1e9) + 10;
8 const int N = 100000 + 10;
9
10 int X[N], idx, Y[N], idy;
11
12 pii p[N];
13 int n;
14
15 std::vector< int> ar[N];
16 std::vector<pii> b[N];
17 int bra[N], brb[N];
18
19 int sum[N];
20
21 struct node {
22 int x, y;
23 node() {};
24 node( int _x, int _y) {
25 x = _x, y = _y;
26 }
27 friend bool operator < ( const node& a, const node& b) {
28 return a.x > b.x;
29 }
30 };
31 std::priority_queue<node> Q;
32
33 int t[N];
34
35 void add( int pos, int v) {
36 if ( 0 == pos) sum[ 0] += v;
37 else {
38 for ( int i = pos; i < idy; i += i & -i)
39 sum[i] += v;
40 }
41 }
42 int query( int pos) {
43 if (pos < 0) return 0;
44 int re = sum[ 0];
45 for ( int i = pos; i > 0; i -= i & -i)
46 re += sum[i];
47 return re;
48 }
49
50 inline int Qu( int L, int R) {
51 return query(R) - query(L - 1);
52 }
53 void work() {
54 for ( int i = 0; i < n; ++i) {
55 scanf( " %d%d ", &p[i].first, &p[i].second);
56 X[i] = p[i].first; Y[i] = p[i].second;
57 }
58 std::sort(X, X + n); std::sort(Y, Y + n);
59 idx = std::unique(X, X + n) - X; idy = std::unique(Y, Y + n) - Y;
60
61 for ( int i = 0; i < n; ++i) {
62 p[i].first = std::lower_bound(X, X + idx, p[i].first) - X;
63 p[i].second = std::lower_bound(Y, Y + idy, p[i].second) - Y;
64 }
65 // 求出所有 垂直线
66 for ( int i = 0; i < idx; ++i) ar[i].clear();
67 for ( int i = 0; i < n; ++i)
68 ar[p[i].first].push_back(p[i].second);
69 for ( int i = 0; i < idx; ++i)
70 std::sort(ar[i].begin(), ar[i].end()); // a[x] (y1, y2)
71
72 memset(brb, - 1, sizeof brb);
73 std::fill(bra, bra + idy + 1, INF);
74
75 for ( int i = 0; i < n; ++i) {
76 bra[p[i].second] = std::min(bra[p[i].second], p[i].first);
77 brb[p[i].second] = std::max(brb[p[i].second], p[i].first);
78 }
79
80 for ( int i = 0; i < idx; ++i) b[i].clear();
81 for ( int i = 0; i < idy; ++i)
82 if (bra[i] < brb[i]) {
83 b[bra[i]].push_back(pii(brb[i], i)); // b[x1] (x2, y)
84 }
85
86 while (!Q.empty()) Q.pop();
87 memset(sum, 0, sizeof sum);
88 memset(t, 0, sizeof t);
89
90 int ans = 0;
91 for ( int i = 0; i < idx; ++i) {
92 for ( int j = 0; j < b[i].size(); ++j) {
93 add(b[i][j].second, 1);
94 ++ t[b[i][j].second];
95 Q.push(node(b[i][j].first, b[i][j].second)); // (edx, y)
96 }
97 while (!Q.empty() && Q.top().x < i) {
98 add(Q.top().y, - 1);
99 -- t[Q.top().y];
100 Q.pop();
101 }
102 if (ar[i].size() > 1) {
103 ans += Qu(ar[i][ 0], ar[i][ar[i].size() - 1]);
104 for ( int j = 0; j < ar[i].size(); ++j) {
105 if ( 0 == t[ar[i][j]])
106 ++ ans;
107 }
108 } else {
109 ans += ar[i].size();
110 }
111
112 }
113 printf( " %d\n ", ans);
114 }
115 int main() {
116 while ( 1 == scanf( " %d ", &n)) {
117 work();
118 }
119 return 0;
120 }
四、贪心
POJ 2698 Servicing DVD Requests
五、数论
1.辗转相除法及其拓展应用
题意:p1(x1,y1)到p2(x2,y2)的线段上有多少个格点(x,y皆为整数)
思路:gcd(x2-x1,y2-y1) - 1代表不连p1,p2时的答案。
x=x2-x1,y=y2-y1 ,假设答案是g,那么x,y都要可以整除g。所以g最大就是gcd(x,y)。
2.高斯消元
hdu 2262
Where is the canteen
基础的高斯求概率。
设某点的期望步数为Ei。
那么目标的Ei=0。
Ei=(Enext1+Enext2……Enextk)/k+1。
整理下就是Enext1+Enext2……Enextk-k*Ei=-k
根据i点的后继结点nextj,那么从所有的后继结点走一步都可以到达i点。便是所有后继的期望平均值+1.
以此建立方程组,然后用高斯消元求解。
2 #include <cstdio>
3 #include <algorithm>
4 #include <vector>
5 #include <cmath>
6
7 typedef std::vector< double > vec;
8 typedef std::vector<vec> mat;
9 const double eps = 1e- 8 ;
10
11 vec gauss_jordan( const mat& A, const vec& b) {
12 int n = A.size();
13 mat B(n, vec(n + 1 ));
14 for ( int i = 0 ; i < n; ++i)
15 for ( int j = 0 ; j < n; ++j)
16 B[i][j] = A[i][j];
17 for ( int i = 0 ; i < n; ++i) B[i][n] = b[i];
18
19 for ( int i = 0 ; i < n; ++i) {
20 int pivot = i;
21 for ( int j = i; j < n; ++j) {
22 if (abs(B[j][i]) > abs(B[pivot][i])) pivot = j;
23 }
24 std::swap(B[i], B[pivot]);
25
26 // 有无穷解或者无解
27 if (abs(B[i][i]) < eps) return vec();
28
29 // 吧正在处理的未知数的系数变成1
30 for ( int j = i + 1 ; j <= n; ++j) B[i][j] /= B[i][i];
31 for ( int j = 0 ; j < n ;++j) {
32 if (i != j) {
33 for ( int k = i + 1 ; k <= n; ++k)
34 B[j][k] -= B[j][i] * B[i][k];
35 }
36 }
37 }
38 vec x(n);
39 for ( int i = 0 ; i < n; ++i) x[i] = B[i][n];
40 return x;
41 }
42
43 const int dx[] = { 1 , 0 , - 1 , 0 };
44 const int dy[] = { 0 , 1 , 0 , - 1 };
45 const int M = 15 + 1 ;
46
47 char s[M][M];
48 bool vis[M][M];
49
50 int r, c;
51
52 void dfs( int x, int y) {
53 vis[x][y] = true ;
54 for ( int dir = 0 ; dir < 4 ; ++dir) {
55 int nx = x + dx[dir], ny = y + dy[dir];
56 if (nx >= 0 && nx < r && ny >= 0 && ny < c && !vis[nx][ny]) {
57 if (s[nx][ny] == ' . ' )
58 dfs(nx, ny);
59 else if (s[nx][ny] == ' $ ' )
60 vis[nx][ny] = true ;
61 }
62 }
63 }
64 void work() {
65 int sx, sy;
66 for ( int i = 0 ; i < r; ++i) {
67 scanf( " %s " , s[i]);
68 for ( int j = 0 ; j < c; ++j)
69 if (s[i][j] == ' @ ' ) {
70 sx = i; sy = j;
71 }
72 }
73
74 memset(vis, 0 , sizeof vis);
75 // printf("%d %d\n", sx, sy);
76 dfs(sx, sy);
77
78 bool f = false ;
79 for ( int i = 0 ; i < r; ++i)
80 for ( int j = 0 ; j < c; ++j)
81 if (s[i][j] == ' $ ' && vis[i][j])
82 f = true ;
83 if (f) {
84 mat A(r * c, vec(r *c, 0 ));
85 vec b(r * c, 0 );
86
87 for ( int i = 0 ; i < r; ++i)
88 for ( int j = 0 ; j < c; ++j) {
89 if (s[i][j] == ' $ ' || !vis[i][j]) {
90 A[i * c + j][i * c + j] = 1 ;
91 continue ;
92 }
93
94 int moves = 0 ;
95 for ( int dir = 0 ; dir < 4 ; ++dir) {
96 int nx = i + dx[dir], ny = j + dy[dir];
97 if (nx >= 0 && nx < r && ny >= 0 && ny < c && vis[nx][ny]) {
98 ++moves;
99 A[i * c + j][nx * c + ny] = - 1 ;
100 }
101 }
102 b[i * c + j] = A[i * c + j][i * c + j] = moves;
103 }
104 vec x = gauss_jordan(A, b);
105 if (x.size() > 0 && fabs(x[sx * c + sy]) > eps) {
106 printf( " %.6f\n " , x[sx * c + sy]);
107 } else {
108 puts( " -1 " );
109 // printf("%.6f\n", x[sx * c + sy]);
110 }
111 } else {
112 puts( " -1 " );
113 }
114 }
115 int main() {
116 while ( 2 == scanf( " %d%d " , &r, &c)) {
117 work();
118 }
119 return 0 ;
120 }
121
122
123 int q(n, m) {
124 if (m == 1 ) return 1 ;
125 else if (m > n) return q(n, n);
126 else return q(n, m - 1 ) + q(n - m, m);
127 }
/******************************华丽的分割线*******************************************/
炫酷算术魔法结社群赛系列(群号283513414)(举办者 ftiasch)
A:POJ 2443 Set Operation
题意:给我一个1000行,10000列的01矩阵,对于指定两列询问是否存在公共的1。
方法:压缩。对于给定两列,如果直接比较需要枚举1000位,我们可以每30位压缩成一个数,用二进制&运算代替枚举,则枚举次数就变成了1000/30 <= 34,起到了非凡的加速效果。
2 #include <algorithm>
3 #include <cstring>
4
5 const int N = 10000 ;
6 const int M = 35 ;
7 int b[N + 1 ][M];
8
9 int n;
10
11 bool check( int x, int y) {
12 for ( int i = 0 ; i < M; ++i)
13 if ((b[x][i] & b[y][i]) > 0 ) return true ;
14 return false ;
15 }
16 void work() {
17 memset(b, 0 , sizeof (b));
18
19 int u, v;
20 for ( int i = 0 ; i < n; ++i) {
21 scanf( " %d " , &u);
22 while (u -- > 0 ) {
23 scanf( " %d " , &v);
24 b[v][i / 30 ] |= 1 << (i % 30 );
25 }
26 }
27 int Q, x, y;
28 scanf( " %d " , &Q);
29 while (Q -- > 0 ) {
30 scanf( " %d%d " , &x, &y);
31 if (check(x, y))
32 puts( " Yes " );
33 else
34 puts( " No " );
35 }
36 }
37 int main() {
38 while ( 1 == scanf( " %d " , &n)) {
39 work();
40 }
41 return 0 ;
42 }
B:POJ 3244 Difference between Triplets
关于这个公式的解释,我们可以将I,J,K看成图上三个点,那么max{I, J, K} - min{I, J, K}就是一条线段的距离,然后线性扫描即可。
2 #include <algorithm>
3 #include <cstring>
4 typedef long long ll;
5 const int N = 200000 ;
6
7 int ab[N], ac[N], bc[N];
8
9 int n;
10
11 void gao(ll& sum, int * num) {
12 for ( int i = 1 ; i < n; ++i) {
13 sum = sum + (ll)(n - i) * i * (num[i] - num[i - 1 ]);
14 }
15 }
16 void work() {
17 int a, b, c;
18 for ( int i = 0 ; i < n; ++i) {
19 scanf( " %d%d%d " , &a, &b, &c);
20 ab[i] = a - b; ac[i] = a - c; bc[i] = b - c;
21 }
22
23 std::sort(ab, ab + n);
24 std::sort(bc, bc + n);
25 std::sort(ac, ac + n);
26
27 ll sum = 0 ;
28 gao(sum, ab);
29 gao(sum, ac);
30 gao(sum, bc);
31
32 printf( " %lld\n " , sum / 2 );
33 }
34 int main() {
35 while ( 1 == scanf( " %d " , &n)) {
36 if ( 0 == n) break ;
37 work();
38 }
39 return 0 ;
40 }
A:POJ 3040 Allowance
B:POJ 3182 The Grove
B:poj 3375
Network Connection
题意:有n台电脑,m个接口,每台电脑和接口都有属于自己的一维坐标,每个电脑都要选择一个接口连接,连接的代价是|Xi - Yj|。
n <= 2000, m <= 100000;
思路:首先O(N*M)的dp肯定可以轻松想到,d[i][j]//用前j个接口来满足前i台电脑后最小需要花费多少
这个算法复杂度太高,所以我们将它优化。我们先在Y中找最接近Xi的那个接口p,对于Xi这才是最佳答案,但是,他可能被其他接口占用,最多有n个接口会占用他,所以,上限是p+n,这个接口也可能要留给其他的接口用,所以,最多有n个接口在后面等着用这个接口,下限是p-n。由此,算法优化到了O(N^2)
2 #include <algorithm>
3 #include <cstring>
4 #include <cmath>
5 typedef long long ll;
6 const ll INF = 1ll << 60 ;
7
8 const int N = 2000 + 10 ;
9 const int M = 100000 + 10 ;
10
11 ll d[N][N << 1 ];
12
13 int L[N], R[N];
14 int n, m;
15
16 int x[N], y[M];
17
18 void work() {
19 for ( int i = 1 ; i <= m; ++i)
20 scanf( " %d " , &y[i]);
21 for ( int i = 1 ; i <= n; ++i)
22 scanf( " %d " , &x[i]);
23
24 std::sort(x + 1 , 1 + x + n);
25 std::sort(y + 1 , 1 + y + m);
26
27 for ( int i = 1 ; i <= n; ++i) {
28 int p = std::lower_bound(y + 1 , y + m + 1 , x[i]) - y;
29 L[i] = std::max( 1 , p - n);
30 R[i] = std::min(m, p + n);
31 }
32
33 for ( int i = 0 ; i < N + N; ++i) d[ 1 ][i] = INF;
34 for ( int i = L[ 1 ]; i <= R[ 1 ]; ++i) d[ 1 ][i - L[ 1 ]] = abs(x[ 1 ] - y[i]);
35
36 for ( int i = 2 ; i <= n; ++i) {
37 int k = L[i - 1 ];
38 ll v = INF;
39
40 for ( int j = L[i]; j <= R[i]; ++j) {
41 while (k <= R[i - 1 ] && k < j) {
42 v = std::min(v, d[i - 1 ][k - L[i - 1 ]]);
43 ++k;
44 }
45 d[i][j - L[i]] = std::min(INF, v + std::abs(x[i] - y[j]));
46 // printf("%d %d %d\n", x[i], y[j], d[i][j]);
47 }
48 }
49 ll mx = INF;
50 for ( int i = L[n]; i <= R[n]; ++i)
51 mx = std::min(mx, d[n][i - L[n]]);
52
53 printf( " %I64d\n " , mx);
54 }
55 int main() {
56 while ( 2 == scanf( " %d%d " , &m, &n)) {
57 work();
58 }
59 return 0 ;
60 }
C:poj 3419 Difference Is Beautiful
题意:给我n个pair型元素,要求我们把他们分成两组(A,B两组),使得A组中first元素的最大值+B组中second元素的最大值的和最小
思路:按照first的值排序,然后枚举A组最大值,线性扫描即可
2 #include <algorithm>
3 #include <cstring>
4 typedef std::pair< int , int > pii;
5 int T = 0 ;
6
7 const int N = 100000 + 10 ;
8 pii ar[N];
9
10 void work() {
11 int n;
12 scanf( " %d " , &n);
13 for ( int i = 0 ; i < n; ++i) {
14 scanf( " %d%d " , &ar[i].first, &ar[i].second);
15 }
16
17 std::sort(ar, ar + n);
18
19 int ans = ( int )(1e9) * 2 + 1 ;
20 int bmax = 0 ;
21 for ( int i = n - 1 ; i >= 0 ; --i) {
22 // printf("%d\n", ans);
23 ans = std::min(ans, ar[i].first + bmax);
24 bmax = std::max(bmax, ar[i].second);
25 }
26 printf( " Case %d: %d\n " , ++T, ans);
27 }
28 int main() {
29 int cas;
30 scanf( " %d " , &cas);
31 while (cas -- > 0 ) {
32 work();
33 }
34 return 0 ;
35 }
题意:用0到255对一个4*N的格子染色,给定M组限制,对于(x1,y1,x2,y2),必须满足(x1,y1)和(x2,y2)两个格子的颜色一样,问有多少种染色方法。(N<=15)
思路:很神奇的dp题。因为要求A x,y >= A x,y-1所以符合单调性。
d[x1][x2][x3][x4]//第一行已经染色了前x1格,第二行..
我们可以先预处理所有不可行的方案,然后每次更新后将这些情况的数值设置为0。
首先第一层是当前要涂的颜色。第二层表示当前要涂的行,从第1行到第4行。
2 #include <algorithm>
3 #include <cstring>
4 #include <vector>
5 #define REP(i, st, ed) for (i = st; i <= ed; ++i)
6 const int mod = 100000 ;
7 const int N = 15 + 1 ;
8 const int M = 100 + 1 ;
9
10 int d[N][N][N][N];
11 bool legal[N][N][N][N];
12
13 int x1[M], x2[M], y1[M], y2[M];
14 int n, m, t = 0 ;
15
16 int p[ 4 ], to[ 4 ];
17 void prepare() {
18 int i;
19
20 REP(p[ 0 ], 0 , n)
21 REP(p[ 1 ], 0 , n)
22 REP(p[ 2 ], 0 , n)
23 REP(p[ 3 ], 0 , n){
24 legal[p[ 0 ]][p[ 1 ]][p[ 2 ]][p[ 3 ]] = false ;
25 REP(i, 1 , m) {
26 if ((p[x1[i]] >= y1[i]) ^ (p[x2[i]] >= y2[i])) {
27 legal[p[ 0 ]][p[ 1 ]][p[ 2 ]][p[ 3 ]] = true ; break ;
28 }
29 }
30 }
31 }
32 void work() {
33 scanf( " %d%d " , &n, &m);
34 for ( int i = 1 ; i <= m; ++i) {
35 scanf( " %d%d%d%d " , &x1[i], &y1[i], &x2[i], &y2[i]);
36 --x1[i]; --x2[i];
37 }
38
39 prepare();
40
41 memset(d, 0 , sizeof d);
42 d[ 0 ][ 0 ][ 0 ][ 0 ] = 1 ;
43
44 for ( int i = 1 ; i <= 256 ; ++i) {
45 for ( int idx = 0 ; idx < 4 ; ++idx)
46 REP(p[ 0 ], 0 , n)
47 REP(p[ 1 ], 0 , n)
48 REP(p[ 2 ], 0 , n)
49 REP(p[ 3 ], 0 , n)
50 if (d[p[ 0 ]][p[ 1 ]][p[ 2 ]][p[ 3 ]] > 0 && p[idx] + 1 <= n) {
51 ++p[idx];
52 int & t = d[p[ 0 ]][p[ 1 ]][p[ 2 ]][p[ 3 ]];
53 --p[idx];
54 t += d[p[ 0 ]][p[ 1 ]][p[ 2 ]][p[ 3 ]];
55 if (t >= mod)
56 t -= mod;
57 }
58 REP(p[ 0 ], 0 , n)
59 REP(p[ 1 ], 0 , n)
60 REP(p[ 2 ], 0 , n)
61 REP(p[ 3 ], 0 , n)
62 if (legal[p[ 0 ]][p[ 1 ]][p[ 2 ]][p[ 3 ]])
63 d[p[ 0 ]][p[ 1 ]][p[ 2 ]][p[ 3 ]] = 0 ;
64 }
65 printf( " Case %d: %05d\n " , ++t, d[n][n][n][n]);
66 }
67 int main() {
68 int cas;
69 scanf( " %d " , &cas);
70 while (cas -- > 0 ) {
71 work();
72 }
73 return 0 ;
74 }
C:HDU 4014 Jimmy’s travel plan
20131003 - 在沙漠中的长途旅行20130904 - 幽默术
C:POJ 3537 Crosses and Crosses
20131005 - 幽默大师职业赛B:POJ 3015 Expected Difference
20131006 - 幽默大师卫冕战C:SGU 379 Elevator
20131007 - 胜利大逃亡
题意:在一个25*25的地图中放置炸弹,消灭所有的山。一个炸弹可以消灭所在行和所在列的所有山。
方法:搜索。假设我们已经选取了i行要放置炸弹,那么还有j行的山要消灭,那么这些山只能靠这i行的炸弹来消灭,如果剩下的炸弹所用的列小于等于i,那么答案就是i。根据这点深搜。
2 #include <cstring>
3 #include <algorithm>
4 const int N = 30 ;
5
6 char s[N];
7 int r, c;
8
9 int sta[N], ans;
10
11 void dfs( int idx, int sx, int cntx, int sy, int cnty) {
12 if (std::max(cntx, cnty) >= ans) return ;
13 if (idx == r) {
14 ans = std::min(ans, std::max(cntx, cnty));
15 } else {
16 if (sta[idx] > 0 ) {
17 int to = sy | sta[idx];
18 if (to == sy)
19 dfs(idx + 1 , sx, cntx, sy, cnty);
20 else {
21 int cc = 0 ;
22 for ( int i = 0 ; i < c; ++i)
23 if (to >> i & 1 )
24 ++cc;
25 dfs(idx + 1 , sx, cntx, to, cc);
26 }
27 dfs(idx + 1 , sx | ( 1 << idx), cntx + 1 , sy, cnty);
28 } else {
29 dfs(idx + 1 , sx, cntx, sy, cnty);
30 }
31 }
32 }
33 void work() {
34 memset(sta, 0 , sizeof sta);
35
36 for ( int i = 0 ; i < r; ++i) {
37 scanf( " %s " , s);
38 for ( int j = 0 ; j < c; ++j)
39 if (s[j] == ' * ' )
40 sta[i] |= 1 << j;
41 }
42
43 ans = std::max(r, c);
44 dfs( 0 , 0 , 0 , 0 , 0 );
45
46 printf( " %d\n " , ans);
47 }
48 int main() {
49 while ( 2 == scanf( " %d%d " , &r, &c)) {
50 work();
51 }
52 return 0 ;
53 }
B:CodeForces 37C Old Berland Language
20131008 - 想不出名字了呜A:HDU 3434 Sequence Adjustment
2013 ACM亚洲赛网络赛系列
F:G(x)
杭州赛区:
J:
Mex
长春赛区:
H:Network
I:Bell
J:
Flyer
二分不幸运的人所在的位置,由于不幸运的人只有一个,所以如果有人不幸运,那么任何从起点开始,终点>=此人位置的区间中,被发放的海报的总数一定是奇数,否则就一定是偶数,由此满足单调性。
2 #include <algorithm>
3 #include <cstring>
4 #include <vector>
5 typedef __int64 ll;
6 const ll INF = 1ll << 32 ;
7 const int N = 20000 + 10 ;
8
9 ll a[N], b[N], c[N];
10 int n;
11
12 bool judge(ll x) {
13 ll tot = 0 ;
14 for ( int i = 0 ; i < n; ++i) {
15 if (x < a[i]) continue ;
16 if (x >= b[i]) {
17 tot = tot + 1 + (b[i] - a[i]) / c[i];
18 } else {
19 tot = tot + 1 + (x - a[i]) / c[i];
20 }
21 tot = tot & 1 ;
22 }
23 return tot & 1 ;
24 }
25 void work() {
26 for ( int i = 0 ; i < n; ++i)
27 scanf( " %I64d%I64d%I64d " , &a[i], &b[i], &c[i]);
28 ll l = 0 , r = INF, mid;
29 while (r - l > 1 ) {
30 mid = (l + r) / 2 ;
31 if (judge(mid))
32 r = mid;
33 else
34 l = mid;
35 }
36
37 if (judge(r)) {
38 int cnt = 0 ;
39 for ( int i = 0 ; i < n; ++i) {
40 if (r < a[i] || r > b[i]) continue ;
41 if ((r - a[i]) % c[i] == 0 )
42 ++cnt;
43 }
44 printf( " %I64d %d\n " , r, cnt);
45 } else
46 puts( " DC Qiang is unhappy. " );
47 }
48 int main() {
49 while ( 1 == scanf( " %d " , &n)) {
50 work();
51 }
52 return 0 ;
53 }
6th BUPT Programming Contest Final
这一题本质上是求两个串有多少对公共回文子串,只考虑子串的起始位置和长度的不同。 给你两个点集问是否相似。
I:
UESTC 1705
The Longest Sequence of Rectangles
题意:n个矩形的lis,需要严格满足第i个矩形的左下角坐标严格大于第i-1个矩形的右上角坐标。
思路:树状数组\线段树。首先将所有点先按照x轴坐标从小到大排序,如果x轴坐标一样,那么终点总是在起点后面,如果是同一性质的点,为了严格递增,按照y轴坐标从大到小排序(如果是不严格则从小到大)。然后用树状数组或者线段树维护。如果遇到的是起点,则更新数组中的值,如果是终点,则将此点插入数据结构中。
2 #include <cstdio>
3 #include <algorithm>
4 struct node {
5 int x, y, typ, id;
6 };
7
8 #define lson l, mid, rt << 1
9 #define rson mid + 1, r, rt << 1 | 1
10 const int N = 200000 + 5 ;
11
12 int val[N];
13
14 node p[N];
15 int y[N], idy;
16
17 int len[N];
18 int query( int R) {
19 int re = 0 ;
20 for (; R > 0 ; R -= R & -R) {
21 re = std::max(re, val[R]);
22 }
23 return re;
24 }
25 void update( int pos, int v) {
26 for (; pos <= idy; pos += pos & -pos)
27 val[pos] = std::max(val[pos], v);
28 }
29 bool cmp( const node& a, const node& b) {
30 if (a.x ^ b.x) return a.x < b.x;
31 else {
32 if (a.typ ^ b.typ) return a.typ < b.typ;
33 else return a.y > b.y;
34 }
35 }
36 void work() {
37 int n;
38 scanf( " %d " , &n);
39
40 idy = 0 ;
41 for ( int i = 0 ; i < n; ++i) {
42 scanf( " %d%d " , &p[i << 1 ].x, &p[i << 1 ].y);
43 p[i << 1 ].typ = 0 ;
44
45 scanf( " %d%d " , &p[i << 1 | 1 ].x, &p[i << 1 | 1 ].y);
46 p[i << 1 | 1 ].typ = 1 ;
47
48 p[i << 1 | 1 ].id = p[i << 1 ].id = i;
49
50 y[idy ++] = p[i << 1 ].y;
51 y[idy ++] = p[i << 1 | 1 ].y;
52 }
53 std::sort(y, y + idy);
54 idy = std::unique(y, y + idy) - y;
55
56 n <<= 1 ;
57 for ( int i = 0 ; i < n; ++i) {
58 p[i].y = std::lower_bound(y, y + idy, p[i].y) - y + 1 ;
59 }
60
61 memset(val, 0 , sizeof ( int ) * (idy + 2 ));
62
63 int mx, ans = 1 ;
64
65 std::sort(p, p + n, cmp);
66 memset(len, 0 , sizeof len);
67 for ( int i = 0 ; i < n; ++i) {
68 if ( 0 == p[i].typ) {
69 len[p[i].id] = std::max(len[p[i].id], 1 + query(p[i].y - 1 ));
70 ans = std::max(ans, len[p[i].id]);
71 } else {
72 update(p[i].y, len[p[i].id]);
73 }
74 }
75 printf( " %d\n " , ans);
76 }
77 int main() {
78 int cas;
79 scanf( " %d " , &cas);
80 while (cas -- > 0 ) {
81 work();
82 }
83 return 0 ;
84 }
ICPC Latin American Regional 2011 - South America/South
题意:
有f种口味的糖果,现在要把每颗糖果分到一些packs里面去。packs分两种:
flavored pack:只有一种口味。
variety pack:每种口味都有。
求满足下列要求的分法有多少种:
1、每个pack至少有两颗糖果。
2、所有pack的糖果数相同。
3、variety pack 里每种口味的糖果数量相同。
4、至少一个variety pack。
5、每种口味至少一个flavored pack。
D:UVALive 5792 Diccionário Portuñol
题意:给你两种串,一种可以当前缀,一种可以当后缀,问两种串合起来,一共有多少种组合。并且没有重合。
题意:给你n个串,现在有一个搜索系统,对于任意一个串s,它能够返回所有包含s这个子串的串的集合。
询问一共有多少种可能的返回集合。对于所有的串s。规模:60个串,每个串长度10^4。