SHOI2008 题目总结

感觉还是上海人出题水平高?这套题写得心旷神怡的。。。总之很难就是啦

由于我实在不适应博客园这种排版和字体。。所以我的文章可能会特别难看大家见谅。。说不定回头开发一个支持全局LaTeX的博客也不错?2333

BZOJ1018 堵塞的交通:

题目大意:有一个2*n的矩阵,初始时没有边,每次可能会打通两个相邻的节点(相邻指曼哈顿距离为1)之间的无向道路或是拆毁两个相邻节点的道路,每次询问两个节点是否连通。

神奇的做法:使用动态树维护整个图的连通性!(这真的可写么?)

正确的做法:由于只有两排,使用一个线段树来维护连通性就可以了。。思路就是线段树的每个叶子节点维护四个节点的C字形的连通性,每个非叶子节点维护这一段的四个顶点的C字型(每个线段树节点其实是一个并查集) 虽然连接方式有很多种情况,但毕竟是有限的几种,手工特判一下就好了。。细节挺多的。特别要注意从两边联通这种情况。随便写一写就好了。

  1 //date 20140624
  2 #include <cstdio>
  3 #include <cstring>
  4  
  5 const int maxn = 105000;
  6  
  7 inline int getint()
  8 {
  9     int ans(0); char w = getchar();
 10     while(w < '0' || '9' < w) w = getchar();
 11     while('0' <= w && w <= '9'){ans = ans * 10 + w - '0'; w = getchar();}
 12     return ans;
 13 }
 14  
 15 template <typename T> inline void swap(T &a, T &b){T x = a; a = b; b = x;}
 16  
 17 /*
 18 0---------2
 19 |         |
 20 |         |
 21 |         |
 22 1---------3
 23 */
 24 int tmp[10];
 25 struct info
 26 {
 27     int num[4];
 28     info(){}
 29     info(int a, int b, int c, int d){num[0] = a; num[1] = b; num[2] = c; num[3] = d;}
 30     void std()
 31     {
 32         memset(tmp, 0, sizeof tmp); int count = 0;
 33         for(int i = 0; i < 4; ++i) if(!tmp[num[i]]) tmp[num[i]] = ++count;
 34         for(int i = 0; i < 4; ++i) num[i] = tmp[num[i]];
 35     }
 36 };
 37  
 38 struct DSU
 39 {
 40     int p[7];
 41     void preset(){  for(int i = 1; i <= 6; ++i) p[i] = i;}
 42     DSU(){preset();}
 43     int getp(int x){return p[x] == x ? x : (p[x] = getp(p[x]));}
 44     void merge(int x, int y){if((x = getp(x)) != (y = getp(y))) p[x] = y;}
 45 };
 46  
 47 inline info operator+ (info A, info B)
 48 {
 49     DSU MEOW;
 50     for(int i = 0; i < 3; ++i) for(int j = i + 1; j < 4; ++j) 
 51         if(A.num[i] == A.num[j]) MEOW.merge(i + 1, j + 1);
 52     for(int i = 0; i < 3; ++i) for(int j = i + 1; j < 4; ++j) 
 53         if(B.num[i] == B.num[j]) MEOW.merge(i + 3, j + 3);
 54     info ans(MEOW.getp(1), MEOW.getp(2), MEOW.getp(5), MEOW.getp(6)); ans.std();
 55     return ans;
 56 }
 57  
 58 int n;
 59 info wius[8];
 60 int now[maxn];
 61  
 62 inline void preset()
 63 {
 64     wius[0] = info(1, 2, 3, 4);
 65     wius[1] = info(1, 2, 1, 3);
 66     wius[2] = info(1, 1, 2, 3);
 67     wius[3] = info(1, 1, 1, 2);
 68     wius[4] = info(1, 2, 3, 2);
 69     wius[5] = info(1, 2, 1, 2);
 70     wius[6] = info(1, 1, 2, 1);
 71     wius[7] = info(1, 1, 1, 1);
 72 }
 73  
 74  
 75 struct zkw_sget
 76 {
 77     int base; info data[300000];
 78     void preset()
 79     {
 80         for(base = 1; base < n + 2; base <<= 1);
 81         for(int i = 1; i < (base << 1); ++i) data[i] = wius[0];
 82     }
 83      
 84     void change(int pos, int val)
 85     {
 86         data[pos + base] = wius[now[pos] = val];
 87         for(int i = (pos + base) >> 1; i; i >>= 1) data[i] = data[i << 1] + data[(i << 1) | 1];
 88     }
 89      
 90     info mini_query(int l, int r)
 91     {
 92         info ansl(wius[5]), ansr(wius[5]);
 93         for(l = l + base - 1, r = r + base + 1; l < r - 1; l >>= 1, r >>= 1)
 94         {
 95             if(!(l & 1)) ansl = ansl + data[l ^ 1];
 96             if( (r & 1)) ansr = data[r ^ 1] + ansr;
 97         }
 98         return ansl + ansr;
 99     }
100      
101     info query(int l, int r)
102     {
103         info ansl, ansm, ansr;
104         ansl = mini_query(1, l - 1);
105         ansm = mini_query(l, r - 1);
106         ansr = mini_query(r, n);
107         if(ansl.num[2] == ansl.num[3]) ansm = wius[7] + ansm;
108         if(ansr.num[0] == ansr.num[1]) ansm = ansm + wius[7];
109         return ansm;
110     }
111 }MEOW;
112  
113 char order[10];
114  
115 int main()
116 {
117     n = getint(); preset();
118     MEOW.preset();
119     while(true)
120     {
121         scanf("%s", order);
122         int x1, y1, x2, y2;
123         switch(order[0])
124         {
125             case 'E': return 0; break;
126             case 'O':  case 'C':
127                 x1 = getint(); y1 = getint(); x2 = getint(); y2 = getint(); 
128                 if(y1 > y2){swap(x1, x2); swap(y1, y2);}
129                 if(y1 == y2) 
130                 {
131                     MEOW.change(y1, now[y1] ^ 2);
132                 }else {
133                     if(x1 == 1) MEOW.change(y1, now[y1] ^ 1);
134                     else MEOW.change(y1, now[y1] ^ 4);
135                 }
136             break;
137  
138             case 'A': 
139                 x1 = getint(); y1 = getint(); x2 = getint(); y2 = getint(); 
140                 if(y1 > y2){swap(x1, x2); swap(y1, y2);}
141                 info tmp = MEOW.query(y1, y2);
142                 printf(tmp.num[x1 - 1] == tmp.num[x2 + 1] ? "Y\n" : "N\n");
143             break;
144         }
145     }
146     return 0;
147 }
traffic

BZOJ1019 汉诺塔:

题目大意:就是正常的三个柱子的汉诺塔,然后一共有六种可能的移动操作,给这些操作排了序,非次选择操作中中合法的(不能把一个大的挪到小的上,不能挪动上一次挪过的盘子)中优先级最高的,问这样多少次可以将所有的盘子挪到另外一个柱子上

首先这样做一定可以挪完是很显然的,而我们每次合法的操作其实不超过3个。由于原版汉诺塔的正解就是递归,那个递归就是DP,所以这东西我们还考虑是否可以DP。非常好,每次状态之和上一次有关,所以显然是可以DP的。

用f[x][i]表示将x号柱子上的从小到大最小的i个盘子整个挪走会挪到哪。

考虑转移方程\[f[x][i] = \left\{ \begin{array}{cl} f[x][i-1] & f[f[x][i-1]][i-1] = x\\ 6-x-f[x][i-1]&else \end{array}\right.\]

这个貌似很显然?首先如果将i-1个盘子挪到某个位置后,一定该挪第i个了,如果再挪那i-1个会挪回原来的位置,那么第i个不可能挪到和i-1相同的位置(那是不合法的),那么只能挪到与x和f[x][i-1]都不同的位置:6 - x - f[x][i - 1]。接下来我们考虑那i-1个应该挪到哪。如果挪回去了,那么第i个也必须挪回去(因为i-1出环了,所以不可能挪到它上面)。

然后dp[x][i]表示将x柱子上的i个盘子挪走需要的最小次数……这就很简单了

 1 //date 20140624
 2 #include <cstdio>
 3 #include <cstring>
 4  
 5 typedef long long ll;
 6 const int maxn = 35;
 7  
 8 ll n;
 9 ll f[maxn][maxn], dp[maxn][maxn];
10 char order[maxn][maxn];
11  
12 int main()
13 {
14     scanf("%lld", &n);
15     for(int i = 1; i <= 6; ++i) 
16     {
17         scanf("%s", order[i] + 1);
18         if(!f[order[i][1] - 'A' + 1][1]) f[order[i][1] - 'A' + 1][1] = order[i][2] - 'A' + 1;
19     }
20     for(int i = 2; i <= n; ++i)
21         for(int x = 1; x <= 3; ++x)
22         {
23             if(f[f[x][i - 1]][i - 1] == x) f[x][i] = f[x][i - 1];
24             else f[x][i] = 6 - x - f[x][i - 1];
25         }
26      
27     dp[1][1] = dp[2][1] = dp[3][1] = 1;
28     for(int i = 2; i <= n; ++i)
29         for(int x = 1; x <= 3; ++x)
30         {
31             if(f[x][i - 1] == f[x][i])
32             {
33                 dp[x][i] = 1 + dp[x][i - 1] + dp[f[x][i]][i - 1] + 1 + dp[x][i - 1];
34             }
35             else dp[x][i] = dp[6 - x - f[x][i]][i - 1] + dp[x][i - 1] + 1;
36         }
37     printf("%lld\n", dp[1][n]);
38     return 0;
39 }
hanoi

 BZOJ1020 安全的航线:

题目大意:有若干个简单多边形(不相交)和一条折线。求这线上在所有多边形外,且距离任意一个多边形的距离的最小值最大的点到它最近的多边形的距离。

这题差点恶心死我。莫涛神犇在论文里提到了一种高效的好写的迭代算法(其实就是队列搜索加剪枝?),有兴趣可以找那篇论文。在NOI吧的资源里面找到2010国家集训队作业里面有。由于是刚学计算几何所以决定用一下传统的做法。

由于是最小值最大,所以首先二分答案d。然后将每个多边形向外扩张d单位长度(边处拓展成矩形,顶点处拓展出扇形,其实直接当圆做就可以),暴力判断当前的区域是否可以覆盖整个折线。

由于常数大了点,险些就TLE了

  1 //date 20140625
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <cmath>
  6 #include <vector>
  7  
  8 using namespace std;
  9 const double EPS = 1e-9;
 10 const double INF = 999999999.0;
 11 const int maxn = 30;
 12  
 13 struct points
 14 {
 15     double x, y;
 16     points(){}
 17     points(double a, double b){x = a; y = b;}
 18     void readin(){scanf("%lf%lf", &x, &y);}
 19     double len2(){return x * x + y * y;}
 20     double len(){return sqrt(len2());}
 21     void print(){printf("%.4lf %.4lf\n", x, y);}
 22 };
 23  
 24 inline int sgn(double x){if(x < -EPS) return -1; if(x < EPS) return 0; return 1;}
 25 inline points operator+ (points A, points B){return points(A.x + B.x, A.y + B.y);}
 26 inline points operator- (points A, points B){return points(A.x - B.x, A.y - B.y);}
 27 inline points operator* (double x, points A){return points(x * A.x, x * A.y);}
 28 inline double operator% (points A, points B){return A.x * B.x + A.y * B.y;}
 29 inline double operator* (points A, points B){return A.x * B.y - A.y * B.x;}
 30 inline int operator< (points A, points B){return fabs(A.x - B.x) > EPS ? B.x - A.x > EPS : B.y - A.y > EPS;}
 31 inline points setlen(double k, points A){return (k / A.len()) * A;}
 32 inline points rot90 (points A){return points(-A.y, A.x);}
 33  
 34 struct seg
 35 {
 36     points P, Q;
 37     seg(){}
 38     seg(points A, points B){P = A; Q = B;}
 39     double len2(){return (P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y);}
 40     double len(){return sqrt(len2());}
 41     void std(){if(Q < P) swap(Q, P);}
 42 }flight[maxn];
 43  
 44 inline int getjd(seg l1, seg l2, points &p)
 45 {
 46     double s1 = (l1.P - l2.P) * (l1.Q - l2.P);
 47     double s2 = (l1.P - l2.Q) * (l1.Q - l2.Q);
 48     if(s1 * s2 > -EPS) return 0;
 49     if((((l2.P - l1.P) * (l2.Q - l1.P)) * ((l2.P - l1.Q) * (l2.Q - l1.Q))) > -EPS) return 0;
 50     p = l2.P + (s1 / (s1 - s2)) * (l2.Q - l2.P);
 51     return 1;
 52 }
 53  
 54 struct polygon
 55 {
 56     vector<seg> s;    int n;
 57 }land[maxn];
 58  
 59 inline int inside(polygon A, points P)
 60 {
 61     int numjd = 0; points tmp;
 62     for(int i = 1; i <= A.n; ++i) 
 63     {
 64         A.s[i].std();
 65         if(sgn((A.s[i].Q - P) * (A.s[i].P - P)) == 1 && sgn(A.s[i].P.x - P.x) <= 0 && sgn(A.s[i].Q.x - P.x) > 0)
 66             numjd++;
 67     }
 68     return numjd & 1;
 69 }
 70  
 71 struct circle
 72 {
 73     points O; double r;
 74     circle(){}
 75     circle(points C, double band){O = C; r = band;}
 76 };
 77  
 78 inline int get_line_jd(circle C, seg l, points &A, points &B)
 79 {
 80     points v = ((C.O - l.P) % (l.Q - l.P) / (l.Q - l.P).len2()) * (l.Q - l.P);
 81     v = v + l.P;
 82     double bxc = C.r * C.r - (C.O - v).len2();
 83     if(bxc < -EPS) return 0;
 84     if(bxc < EPS) {A = v; return 1;}
 85     A = v - setlen(sqrt(bxc), l.Q - l.P); B = v + setlen(sqrt(bxc), l.Q - l.P); return 2;
 86 }
 87  
 88 inline int getjd(circle C, seg l, points &A, points &B)
 89 {
 90     if(get_line_jd(C, l, A, B) < 2) return 0;
 91     l.std();
 92     if(sgn(A.x - l.P.x) * sgn(A.x - l.Q.x) == 1) return 0;
 93     if(sgn(B.x - l.P.x) * sgn(B.x - l.Q.x) == 1) return 0;
 94     return 1;
 95 }
 96  
 97 int n, m;
 98 vector<pair<points, int> > check;
 99 vector<points> db;
100  
101 inline void work1(polygon A, seg l)
102 {
103     db.clear(); l.std(); points tmp;
104     for(int i = 1; i <= A.n; ++i) A.s[i].std();
105     db.push_back(l.P); db.push_back(l.Q);
106     for(int i = 1; i <= A.n; ++i) if(getjd(A.s[i], l, tmp)) db.push_back(tmp);
107     sort(db.begin(), db.end());
108     for(int i = 0; i + 1 < db.size(); ++i)
109     {
110         if(db[i] < db[i + 1])
111         {
112             tmp = 0.5 * (db[i] + db[i + 1]);
113             if(inside(A, tmp))
114             {
115                 check.push_back(make_pair(db[i], 1));
116                 check.push_back(make_pair(db[i + 1], -1));
117 //              printf("\t\t%.4lf %.4lf %.4lf %.4lf\n", db[i].x, db[i].y, db[i + 1].x, db[i + 1].y);
118             }
119         }
120     }
121 }
122  
123 inline void work2(circle C, seg l)
124 {
125     points A, B;
126     if(get_line_jd(C, l, A, B) != 2) return;
127     if(B < A) swap(A, B);
128     l.std();
129     if(A < l.P) A = l.P;
130     if(l.Q < B) B = l.Q;
131     if(A < B) 
132     {
133         check.push_back(make_pair(A, 1)); check.push_back(make_pair(B, -1)); 
134 /*      printf("\n===============\n");
135         A.print(); B.print();
136         printf("===============\n\n");*/
137     }
138 }
139  
140 inline int check_it(double mid)
141 {
142     polygon tmp;
143     for(int i = 1; i <= n; ++i)
144     {
145         check.clear(); 
146         check.push_back(make_pair(flight[i].P, 0)); check.push_back(make_pair(flight[i].Q, 0));
147         for(int j = 1; j <= m; ++j)
148         {
149             work1(land[j], flight[i]);
150             for(int k = 1; k <= land[j].n; ++k)
151             {
152                 tmp.s.clear(); tmp.s.resize(5);
153                 tmp.n = 4;
154                 points v = setlen(mid, rot90(land[j].s[k].Q - land[j].s[k].P));
155                 tmp.s[1] = seg(land[j].s[k].Q + v, land[j].s[k].P + v);
156                 tmp.s[3] = seg(land[j].s[k].P - v, land[j].s[k].Q - v);
157                 tmp.s[2] = seg(tmp.s[1].Q, tmp.s[3].P);
158                 tmp.s[4] = seg(tmp.s[3].Q, tmp.s[1].P);
159                 work1(tmp, flight[i]);
160             }
161             for(int k = 1; k <= land[j].n; ++k)
162                 work2(circle(land[j].s[k].P, mid), flight[i]);
163         }
164         sort(check.begin(), check.end());
165         int sum = 0;
166         for(int j = 0; j + 1 < check.size(); ++j)
167         {
168             sum += check[j].second;
169             if(check[j].first < check[j + 1].first && sum <= 0)
170             {
171                 return 0;
172             }
173         }
174 //      for(int w = 0; w < check.size(); ++w) printf("%.4lf %.4lf %d\n", check[w].first.x, check[w].first.y, check[w].second);
175 //      printf("\n\n\n");
176     }
177     return 1;
178 }
179  
180 int main()
181 {
182     scanf("%d%d", &m, &n);
183     for(int i = 1; i < n; ++i) flight[i].P.readin(); --n; flight[n].Q.readin();
184     for(int i = 1; i < n; ++i) flight[i].Q = flight[i + 1].P;
185      
186     for(int i = 1; i <= m; ++i)
187     {
188         scanf("%d", &land[i].n);
189         land[i].s.resize(land[i].n + 1);
190         for(int j = 1; j <= land[i].n; ++j)
191             land[i].s[j].P.readin();
192         for(int j = 1; j < land[i].n; ++j) land[i].s[j].Q = land[i].s[j + 1].P;
193         land[i].s[land[i].n].Q = land[i].s[1].P;
194     }
195      
196 //  check_it(3);
197      
198     double Llim = 0, Rlim = INF, mid;
199     while(Rlim - Llim > EPS)
200     {
201         mid = (Llim + Rlim) * 0.5;
202         if(check_it(mid)) Rlim = mid;
203         else Llim = mid;
204     }
205      
206     printf("%.2lf\n", Llim);
207     return 0;
208 }
flight

BZOJ1021 循环的债务:

题目大意:A欠B钱,B欠C钱,C欠A钱。给出每个人手中拥有的100、50、20、10、5、1元纸币数量,然后问最多交换多少张纸币可以请算债务

首先无论怎样都可以归结为两个人欠一个人钱或者一个人欠两个人钱。从小到大处理每一种纸币。设dp[i][sa][sb]表示处理到第i种纸币,a共有sa元,b有sb元的少交换次数。由纸币的面值决定了,使用这种纸币时必须保证当前的欠款整除它和后面所有面值的gcd(因为小于gcd的必须在这种面值之前被搞定)然后转移方程有点复杂……我分了6种情况讨论的(两个人欠一个人钱或者一个人欠两个人钱)

 1 //date 20140625
 2 #include <cstdio>
 3 #include <cstring>
 4  
 5 const int maxn = 1050;
 6 const int maxm = 7;
 7 const int INF = 0x7F7F7F7F;
 8 const int money[] = {1, 5, 10, 20, 50, 100};
 9 const int gcd[] = {1, 5, 10, 10, 50, 100};
10  
11 inline int denew(int &a, int b){if(a > b){a = b; return 1;} return 0;}
12 int x1, x2, x3, sa, sb, sc, ea, eb, ec, sum;
13 int a[maxm], b[maxm], c[maxm];
14 int dp[2][maxn][maxn];
15  
16 int main()
17 {
18     scanf("%d%d%d", &x1, &x2, &x3);
19     ea -= x1; eb += x1; eb -= x2; ec += x2; ec -= x3; ea += x3;
20     for(int i = 5; i >= 0; --i){scanf("%d", &a[i]); sa += a[i] * money[i]; ea += a[i] * money[i];}
21     for(int i = 5; i >= 0; --i){scanf("%d", &b[i]); sb += b[i] * money[i]; eb += b[i] * money[i];}
22     for(int i = 5; i >= 0; --i){scanf("%d", &c[i]); sc += c[i] * money[i]; ec += c[i] * money[i];}
23  
24     sum = sa + sb + sc;
25     if(ea < 0 || eb < 0 || ec < 0){printf("impossible\n"); return 0;}
26      
27     int now = 0;
28     memset(dp, 0x7F, sizeof dp);
29     dp[now][sa][sb] = 0;
30     for(int i = 0; i < 6; ++i)
31     {
32         for(int j = 0; j < maxn; ++j) for(int k = 0; k < maxn; ++k) dp[now ^ 1][j][k] = dp[now][j][k];
33         int x = 0, y = 0;
34         while((ea - x) % gcd[i]) ++x;
35         while((eb - y) % gcd[i]) ++y;
36         if((ec - (sum - x - y)) % gcd[i]) continue;
37         for(int j = x; j < maxn; j += gcd[i])
38         {
39             for(int k = y; k < maxn; k += gcd[i])
40             {
41                 int vc = sum - j - k;
42                 if(vc < 0) break;
43                 if(dp[now][j][k] == INF) continue;
44                 for(int r = 0; r <= a[i]; ++r) if(j >= money[i] * r) 
45                     for(int l = 0; l <= r; ++l) 
46                         if(k + money[i] * l < maxn && vc + money[i] * (r - l) < maxn)
47                             denew(dp[now ^ 1][j - money[i] * r][k + money[i] * l], dp[now][j][k] + r);
48                              
49                 for(int r = 0; r <= b[i]; ++r) if(k >= money[i] * r) 
50                     for(int l = 0; l <= r; ++l) 
51                         if(j + money[i] * l < maxn && vc + money[i] * (r - l) < maxn)
52                             denew(dp[now ^ 1][j + money[i] * l][k - money[i] * r], dp[now][j][k] + r);
53  
54                 for(int r = 0; r <= c[i]; ++r) if(vc >= money[i] * r)
55                     for(int l = 0; l <= r; ++l)
56                         if(j + money[i] * l < maxn && k + money[i] * (r - l) < maxn)
57                             denew(dp[now ^ 1][j + money[i] * l][k + money[i] * (r - l)], dp[now][j][k] + r);
58                  
59                 for(int r = 0; r <= a[i]; ++r) if(j >= money[i] * r)
60                     for(int l = 0; l <= b[i]; ++l) 
61                         if(k >= money[i] * l && vc + money[i] * (r + l) < maxn) 
62                             denew(dp[now ^ 1][j - money[i] * r][k - money[i] * l], dp[now][j][k] + l + r);
63                  
64                 for(int r = 0; r <= a[i]; ++r) if(j >= money[i] * r)
65                     for(int l = 0; l <= c[i]; ++l) 
66                         if(vc >= money[i] * l && k + money[i] * (l + r) < maxn)
67                             denew(dp[now ^ 1][j - money[i] * r][k + money[i] * (l + r)], dp[now][j][k] + l + r);
68  
69                 for(int r = 0; r <= b[i]; ++r) if(k >= money[i] * r)
70                     for(int l = 0; l <= c[i]; ++l)
71                         if(vc >= money[i] * l && j + money[i] * (l + r) < maxn)
72                             denew(dp[now ^ 1][j + money[i] * (l + r)][k - money[i] * r], dp[now][j][k] + l + r);
73             }
74         }
75         now ^= 1;
76          
77     }
78      
79     if(dp[now][ea][eb] == INF) printf("impossible\n");
80     else printf("%d\n", dp[now][ea][eb]);
81     return 0;
82 }
debt

BZOJ1022 小约翰的游戏:

题目大意:反Nim游戏判断输赢

结论题:如果所有石子数异或和为1且没有某堆石子数量>1,或者某堆石子数量>1但是总数量异或和为0时,后手胜,否则先手必胜

数学归纳法就能证明了

 1 //date 20140626
 2 #include <cstdio>
 3 #include <cstring>
 4  
 5 const int maxn = 60;
 6  
 7 int t, n;
 8 int st[maxn];
 9  
10 int main()
11 {
12     scanf("%d", &t);
13     for(int tc = 1; tc <= t; ++tc)
14     {
15         scanf("%d", &n);
16         int x, type = 0, sum = 0;
17         for(int i = 1; i <= n; ++i) {scanf("%d", &x); if(x > 1) type = 1; sum ^= x;}
18         sum = sum > 0;
19         if(type ^ sum) printf("Brother\n"); else printf("John\n");
20     }
21      
22     return 0;
23 }
John

BZOJ1023 仙人掌图:

题目大意:求给定仙人掌的直径

标准的做法应该是DFS出一棵生成树然后DP。但是为了处理更一般的仙人掌问题我采用了点双缩点然后树+环DP的方法。

首先缩点之后会得到一棵生成树,先计算出每个点向下走的最大距离。然后计算出每个点向上走的最大距离,用一个单调队列就可以了(时间仓促没说清楚,过两天另开个文章说这题好了)

  1 //date 20140627
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <vector>
  5  
  6 using namespace std;
  7 const int maxn = 105000;
  8 const int maxm = maxn << 2;
  9  
 10 inline int getint()
 11 {
 12     int ans(0); char w = getchar();
 13     while(w < '0' || '9' < w) w = getchar();
 14     while('0' <= w && w <= '9')   {ans = ans * 10 + w - '0'; w = getchar();}
 15     return ans;
 16 }
 17  
 18 inline int min(int a, int b){return a < b ? a : b;}
 19 inline int denew(int &a, int b){if(a > b){a = b; return 1;} return 0;}
 20 inline int innew(int &a, int b){if(a < b){a = b; return 1;} return 0;}
 21  
 22 int n, m;
 23 struct edge {int v, next;}E1[maxm], E2[maxm];
 24 int a1[maxn], a2[maxn], nedge1, nedge2;
 25 inline void add1(int u, int v){E1[++nedge1].v = v; E1[nedge1].next = a1[u]; a1[u] = nedge1;}
 26 inline void add2(int u, int v){E2[++nedge2].v = v; E2[nedge2].next = a2[u]; a2[u] = nedge2;}
 27 int dfn[maxn], low[maxn], ncol, timer, dpt[maxn], p[maxn], q[maxn], ind[maxn];
 28 int dp1[maxn], dp2[maxn], dq[maxn], maxval[maxn], cmaxval[maxn];
 29 vector<int> cyc[maxn], tmp;
 30  
 31 inline void tarjan(int v0)
 32 {
 33     static int d[maxn], now[maxn], stack[maxn], ins[maxn], stop, last, i, j;
 34     memcpy(now, a1, sizeof a1);
 35     for(dfn[v0] = low[v0] = timer += ins[d[last = 1] = stack[stop = 1] = v0] = 1; last; )
 36     {
 37         if(!(j = now[i = d[last]]))
 38         {
 39             if(--last)
 40             {
 41                 if(dfn[d[last]] == low[i])
 42                 {
 43                     for(++ncol; ins[i]; ins[stack[stop--]] = 0)
 44                     {
 45                         add2(n + ncol, stack[stop]); add2(stack[stop], n + ncol);
 46                     }
 47                     add2(n + ncol, d[last]); add2(d[last], n + ncol);
 48                 }
 49                 denew(low[d[last]], low[i]);
 50             }
 51             continue;
 52         }
 53         if(!dfn[E1[j].v])
 54         {
 55             ins[d[++last] = stack[++stop] = E1[j].v] = 1; low[E1[j].v] = dfn[E1[j].v] = ++timer;
 56         }else denew(low[i], dfn[E1[j].v]);
 57         now[i] = E1[j].next;
 58     }
 59 }
 60  
 61 inline void DFS1(int v0)
 62 {
 63     static int d[maxn], now[maxn], last, i, j;
 64     memcpy(now, a2, sizeof a2);
 65     for(dpt[d[last = 1] = v0] = 1; last; )
 66     {
 67         if(!(j = now[i = d[last]])){--last; continue;}
 68         if(!dpt[E2[j].v]) 
 69         {
 70             dpt[E2[j].v] = dpt[p[d[++last] = E2[j].v] = i] + 1;
 71             if(E2[j].v > n)
 72             {
 73                 for(int w = a2[E2[j].v], sgn = 0; w; w = E2[w].next)
 74                 {
 75                     if(E2[w].v == i) sgn = 1;
 76                     if(sgn) cyc[E2[j].v].push_back(E2[w].v);
 77                 }
 78                 for(int w = a2[E2[j].v]; w; w = E2[w].next)
 79                 {
 80                     if(E2[w].v == i) break;
 81                     cyc[E2[j].v].push_back(E2[w].v);
 82                 }
 83             }
 84             ++ind[i];
 85         }
 86         now[i] = E2[j].next;
 87     }
 88 }
 89  
 90 inline void BFS1()
 91 {
 92     int st = 0, ed = 0;
 93     for(int i = 1; i <= n + ncol; ++i) if(!ind[i]) q[++ed] = i;
 94     while(st < ed)
 95     {
 96         int x = q[++st];
 97         if(!(--ind[p[x]])) q[++ed] = p[x];
 98     }
 99 }
100  
101 inline void dynamic_programming1()
102 {
103     for(int t = 1, i = q[1]; t <= n + ncol; i = q[++t]) if(i > n)
104     {
105         for(int j = 1; j < cyc[i].size(); ++j) innew(dp1[i], dp1[cyc[i][j]] + min(j, cyc[i].size() - j));
106         innew(dp1[p[i]], dp1[i]);
107     }
108 }
109  
110 inline void dynamic_programming2()
111 {
112     dp2[1] = 0;
113     int st, ed;
114     for(int t = n + ncol, i = q[n + ncol]; t; i = q[--t])
115     {
116         if(i > n)
117         {
118             if(dp1[i] == maxval[p[i]]) innew(dp2[i], cmaxval[p[i]]);
119             else innew(dp2[i], maxval[p[i]]);
120              
121             tmp.clear();
122             int tsz = cyc[i].size(), htsz = tsz >> 1; 
123             for(int w = 1; w <= 3; ++w)
124             {
125                 tmp.push_back(dp2[i] - (w - 1) * tsz);
126                 for(int j = 1; j < tsz; ++j) tmp.push_back(dp1[cyc[i][j]] - j - (w - 1) * tsz);
127             }
128             st = ed = 0;
129             for(int j = tsz - htsz + 1; j <= tsz; ++j)
130             {
131                 while(st < ed && tmp[dq[ed]] < tmp[j]) --ed;
132                 dq[++ed] = j; 
133             }
134             for(int j = tsz + 1; j < (tsz << 1); ++j)
135             {
136                 while(st < ed && dq[st + 1] < j - htsz) ++st;
137                 if(st < ed) innew(dp2[cyc[i][j - tsz]], j + tmp[dq[st + 1]]);
138                 while(st < ed && tmp[dq[ed]] < tmp[j]) --ed;
139                 dq[++ed] = j;
140             }
141  
142             tmp.clear();
143             for(int w = 1; w <= 3; ++w)
144             {
145                 tmp.push_back(dp2[i] + (w - 1) * tsz);
146                 for(int j = 1; j < tsz; ++j) tmp.push_back(dp1[cyc[i][j]] + j + (w - 1) * tsz);
147             }
148             st = ed = 0;
149             for(int j = (tsz << 1) + htsz - 1; j >= (tsz << 1); --j)
150             {
151                 while(st < ed && tmp[dq[ed]] < tmp[j]) --ed;
152                 dq[++ed] = j; 
153             }
154             for(int j = (tsz << 1) - 1; j > tsz; --j)
155             {
156                 while(st < ed && dq[st + 1] > j + htsz) ++st;
157                 if(st < ed) innew(dp2[cyc[i][j - tsz]], -j + tmp[dq[st + 1]]);
158                 while(st < ed && tmp[dq[ed]] < tmp[j]) --ed;
159                 dq[++ed] = j;
160             }
161         }else{
162             maxval[i] = dp2[i];
163             for(int j = a2[i]; j; j = E2[j].next) if(E2[j].v != p[i])
164             {
165                 if(dp1[E2[j].v] > maxval[i])
166                 {
167                     cmaxval[i] = maxval[i];
168                     maxval[i] = dp1[E2[j].v];
169                 }else innew(cmaxval[i], dp1[E2[j].v]);
170             }
171         }
172     }
173 }
174  
175 int main()
176 {
177     n = getint(); m = getint();
178     for(int i = 1; i <= m; ++i)
179     {
180         int x = getint(), last = getint(), now;
181         for(int i = 1; i < x; ++i)
182         {
183             now = getint();
184             add1(now, last); add1(last, now);
185             last = now;
186         }
187     }
188      
189     tarjan(1); DFS1(1); BFS1();
190     dynamic_programming1();
191     dynamic_programming2();
192      
193     int ans = 0;
194     for(int i = 1; i <= ncol; ++i) innew(ans, dp2[i + n] + dp1[i + n]);
195     printf("%d\n", ans);
196     return 0;
197 }
cactus

 

posted on 2014-06-30 00:03  SnowyJone  阅读(537)  评论(0编辑  收藏  举报