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

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

BZOJ1018 堵塞的交通:

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

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

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

 //date 20140624
#include <cstdio>
#include <cstring> const int maxn = ; inline int getint()
{
int ans(); char w = getchar();
while(w < '' || '' < w) w = getchar();
while('' <= w && w <= ''){ans = ans * + w - ''; w = getchar();}
return ans;
} template <typename T> inline void swap(T &a, T &b){T x = a; a = b; b = x;} /*
0---------2
| |
| |
| |
1---------3
*/
int tmp[];
struct info
{
int num[];
info(){}
info(int a, int b, int c, int d){num[] = a; num[] = b; num[] = c; num[] = d;}
void std()
{
memset(tmp, , sizeof tmp); int count = ;
for(int i = ; i < ; ++i) if(!tmp[num[i]]) tmp[num[i]] = ++count;
for(int i = ; i < ; ++i) num[i] = tmp[num[i]];
}
}; struct DSU
{
int p[];
void preset(){ for(int i = ; i <= ; ++i) p[i] = i;}
DSU(){preset();}
int getp(int x){return p[x] == x ? x : (p[x] = getp(p[x]));}
void merge(int x, int y){if((x = getp(x)) != (y = getp(y))) p[x] = y;}
}; inline info operator+ (info A, info B)
{
DSU MEOW;
for(int i = ; i < ; ++i) for(int j = i + ; j < ; ++j)
if(A.num[i] == A.num[j]) MEOW.merge(i + , j + );
for(int i = ; i < ; ++i) for(int j = i + ; j < ; ++j)
if(B.num[i] == B.num[j]) MEOW.merge(i + , j + );
info ans(MEOW.getp(), MEOW.getp(), MEOW.getp(), MEOW.getp()); ans.std();
return ans;
} int n;
info wius[];
int now[maxn]; inline void preset()
{
wius[] = info(, , , );
wius[] = info(, , , );
wius[] = info(, , , );
wius[] = info(, , , );
wius[] = info(, , , );
wius[] = info(, , , );
wius[] = info(, , , );
wius[] = info(, , , );
} struct zkw_sget
{
int base; info data[];
void preset()
{
for(base = ; base < n + ; base <<= );
for(int i = ; i < (base << ); ++i) data[i] = wius[];
} void change(int pos, int val)
{
data[pos + base] = wius[now[pos] = val];
for(int i = (pos + base) >> ; i; i >>= ) data[i] = data[i << ] + data[(i << ) | ];
} info mini_query(int l, int r)
{
info ansl(wius[]), ansr(wius[]);
for(l = l + base - , r = r + base + ; l < r - ; l >>= , r >>= )
{
if(!(l & )) ansl = ansl + data[l ^ ];
if( (r & )) ansr = data[r ^ ] + ansr;
}
return ansl + ansr;
} info query(int l, int r)
{
info ansl, ansm, ansr;
ansl = mini_query(, l - );
ansm = mini_query(l, r - );
ansr = mini_query(r, n);
if(ansl.num[] == ansl.num[]) ansm = wius[] + ansm;
if(ansr.num[] == ansr.num[]) ansm = ansm + wius[];
return ansm;
}
}MEOW; char order[]; int main()
{
n = getint(); preset();
MEOW.preset();
while(true)
{
scanf("%s", order);
int x1, y1, x2, y2;
switch(order[])
{
case 'E': return ; break;
case 'O': case 'C':
x1 = getint(); y1 = getint(); x2 = getint(); y2 = getint();
if(y1 > y2){swap(x1, x2); swap(y1, y2);}
if(y1 == y2)
{
MEOW.change(y1, now[y1] ^ );
}else {
if(x1 == ) MEOW.change(y1, now[y1] ^ );
else MEOW.change(y1, now[y1] ^ );
}
break; case 'A':
x1 = getint(); y1 = getint(); x2 = getint(); y2 = getint();
if(y1 > y2){swap(x1, x2); swap(y1, y2);}
info tmp = MEOW.query(y1, y2);
printf(tmp.num[x1 - ] == tmp.num[x2 + ] ? "Y\n" : "N\n");
break;
}
}
return ;
}

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个盘子挪走需要的最小次数……这就很简单了

 //date 20140624
#include <cstdio>
#include <cstring> typedef long long ll;
const int maxn = ; ll n;
ll f[maxn][maxn], dp[maxn][maxn];
char order[maxn][maxn]; int main()
{
scanf("%lld", &n);
for(int i = ; i <= ; ++i)
{
scanf("%s", order[i] + );
if(!f[order[i][] - 'A' + ][]) f[order[i][] - 'A' + ][] = order[i][] - 'A' + ;
}
for(int i = ; i <= n; ++i)
for(int x = ; x <= ; ++x)
{
if(f[f[x][i - ]][i - ] == x) f[x][i] = f[x][i - ];
else f[x][i] = - x - f[x][i - ];
} dp[][] = dp[][] = dp[][] = ;
for(int i = ; i <= n; ++i)
for(int x = ; x <= ; ++x)
{
if(f[x][i - ] == f[x][i])
{
dp[x][i] = + dp[x][i - ] + dp[f[x][i]][i - ] + + dp[x][i - ];
}
else dp[x][i] = dp[ - x - f[x][i]][i - ] + dp[x][i - ] + ;
}
printf("%lld\n", dp[][n]);
return ;
}

hanoi

 BZOJ1020 安全的航线:

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

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

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

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

 //date 20140625
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector> using namespace std;
const double EPS = 1e-;
const double INF = 999999999.0;
const int maxn = ; struct points
{
double x, y;
points(){}
points(double a, double b){x = a; y = b;}
void readin(){scanf("%lf%lf", &x, &y);}
double len2(){return x * x + y * y;}
double len(){return sqrt(len2());}
void print(){printf("%.4lf %.4lf\n", x, y);}
}; inline int sgn(double x){if(x < -EPS) return -; if(x < EPS) return ; return ;}
inline points operator+ (points A, points B){return points(A.x + B.x, A.y + B.y);}
inline points operator- (points A, points B){return points(A.x - B.x, A.y - B.y);}
inline points operator* (double x, points A){return points(x * A.x, x * A.y);}
inline double operator% (points A, points B){return A.x * B.x + A.y * B.y;}
inline double operator* (points A, points B){return A.x * B.y - A.y * B.x;}
inline int operator< (points A, points B){return fabs(A.x - B.x) > EPS ? B.x - A.x > EPS : B.y - A.y > EPS;}
inline points setlen(double k, points A){return (k / A.len()) * A;}
inline points rot90 (points A){return points(-A.y, A.x);} struct seg
{
points P, Q;
seg(){}
seg(points A, points B){P = A; Q = B;}
double len2(){return (P.x - Q.x) * (P.x - Q.x) + (P.y - Q.y) * (P.y - Q.y);}
double len(){return sqrt(len2());}
void std(){if(Q < P) swap(Q, P);}
}flight[maxn]; inline int getjd(seg l1, seg l2, points &p)
{
double s1 = (l1.P - l2.P) * (l1.Q - l2.P);
double s2 = (l1.P - l2.Q) * (l1.Q - l2.Q);
if(s1 * s2 > -EPS) return ;
if((((l2.P - l1.P) * (l2.Q - l1.P)) * ((l2.P - l1.Q) * (l2.Q - l1.Q))) > -EPS) return ;
p = l2.P + (s1 / (s1 - s2)) * (l2.Q - l2.P);
return ;
} struct polygon
{
vector<seg> s; int n;
}land[maxn]; inline int inside(polygon A, points P)
{
int numjd = ; points tmp;
for(int i = ; i <= A.n; ++i)
{
A.s[i].std();
if(sgn((A.s[i].Q - P) * (A.s[i].P - P)) == && sgn(A.s[i].P.x - P.x) <= && sgn(A.s[i].Q.x - P.x) > )
numjd++;
}
return numjd & ;
} struct circle
{
points O; double r;
circle(){}
circle(points C, double band){O = C; r = band;}
}; inline int get_line_jd(circle C, seg l, points &A, points &B)
{
points v = ((C.O - l.P) % (l.Q - l.P) / (l.Q - l.P).len2()) * (l.Q - l.P);
v = v + l.P;
double bxc = C.r * C.r - (C.O - v).len2();
if(bxc < -EPS) return ;
if(bxc < EPS) {A = v; return ;}
A = v - setlen(sqrt(bxc), l.Q - l.P); B = v + setlen(sqrt(bxc), l.Q - l.P); return ;
} inline int getjd(circle C, seg l, points &A, points &B)
{
if(get_line_jd(C, l, A, B) < ) return ;
l.std();
if(sgn(A.x - l.P.x) * sgn(A.x - l.Q.x) == ) return ;
if(sgn(B.x - l.P.x) * sgn(B.x - l.Q.x) == ) return ;
return ;
} int n, m;
vector<pair<points, int> > check;
vector<points> db; inline void work1(polygon A, seg l)
{
db.clear(); l.std(); points tmp;
for(int i = ; i <= A.n; ++i) A.s[i].std();
db.push_back(l.P); db.push_back(l.Q);
for(int i = ; i <= A.n; ++i) if(getjd(A.s[i], l, tmp)) db.push_back(tmp);
sort(db.begin(), db.end());
for(int i = ; i + < db.size(); ++i)
{
if(db[i] < db[i + ])
{
tmp = 0.5 * (db[i] + db[i + ]);
if(inside(A, tmp))
{
check.push_back(make_pair(db[i], ));
check.push_back(make_pair(db[i + ], -));
// printf("\t\t%.4lf %.4lf %.4lf %.4lf\n", db[i].x, db[i].y, db[i + 1].x, db[i + 1].y);
}
}
}
} inline void work2(circle C, seg l)
{
points A, B;
if(get_line_jd(C, l, A, B) != ) return;
if(B < A) swap(A, B);
l.std();
if(A < l.P) A = l.P;
if(l.Q < B) B = l.Q;
if(A < B)
{
check.push_back(make_pair(A, )); check.push_back(make_pair(B, -));
/* printf("\n===============\n");
A.print(); B.print();
printf("===============\n\n");*/
}
} inline int check_it(double mid)
{
polygon tmp;
for(int i = ; i <= n; ++i)
{
check.clear();
check.push_back(make_pair(flight[i].P, )); check.push_back(make_pair(flight[i].Q, ));
for(int j = ; j <= m; ++j)
{
work1(land[j], flight[i]);
for(int k = ; k <= land[j].n; ++k)
{
tmp.s.clear(); tmp.s.resize();
tmp.n = ;
points v = setlen(mid, rot90(land[j].s[k].Q - land[j].s[k].P));
tmp.s[] = seg(land[j].s[k].Q + v, land[j].s[k].P + v);
tmp.s[] = seg(land[j].s[k].P - v, land[j].s[k].Q - v);
tmp.s[] = seg(tmp.s[].Q, tmp.s[].P);
tmp.s[] = seg(tmp.s[].Q, tmp.s[].P);
work1(tmp, flight[i]);
}
for(int k = ; k <= land[j].n; ++k)
work2(circle(land[j].s[k].P, mid), flight[i]);
}
sort(check.begin(), check.end());
int sum = ;
for(int j = ; j + < check.size(); ++j)
{
sum += check[j].second;
if(check[j].first < check[j + ].first && sum <= )
{
return ;
}
}
// for(int w = 0; w < check.size(); ++w) printf("%.4lf %.4lf %d\n", check[w].first.x, check[w].first.y, check[w].second);
// printf("\n\n\n");
}
return ;
} int main()
{
scanf("%d%d", &m, &n);
for(int i = ; i < n; ++i) flight[i].P.readin(); --n; flight[n].Q.readin();
for(int i = ; i < n; ++i) flight[i].Q = flight[i + ].P; for(int i = ; i <= m; ++i)
{
scanf("%d", &land[i].n);
land[i].s.resize(land[i].n + );
for(int j = ; j <= land[i].n; ++j)
land[i].s[j].P.readin();
for(int j = ; j < land[i].n; ++j) land[i].s[j].Q = land[i].s[j + ].P;
land[i].s[land[i].n].Q = land[i].s[].P;
} // check_it(3); double Llim = , Rlim = INF, mid;
while(Rlim - Llim > EPS)
{
mid = (Llim + Rlim) * 0.5;
if(check_it(mid)) Rlim = mid;
else Llim = mid;
} printf("%.2lf\n", Llim);
return ;
}

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种情况讨论的(两个人欠一个人钱或者一个人欠两个人钱)

 //date 20140625
#include <cstdio>
#include <cstring> const int maxn = ;
const int maxm = ;
const int INF = 0x7F7F7F7F;
const int money[] = {, , , , , };
const int gcd[] = {, , , , , }; inline int denew(int &a, int b){if(a > b){a = b; return ;} return ;}
int x1, x2, x3, sa, sb, sc, ea, eb, ec, sum;
int a[maxm], b[maxm], c[maxm];
int dp[][maxn][maxn]; int main()
{
scanf("%d%d%d", &x1, &x2, &x3);
ea -= x1; eb += x1; eb -= x2; ec += x2; ec -= x3; ea += x3;
for(int i = ; i >= ; --i){scanf("%d", &a[i]); sa += a[i] * money[i]; ea += a[i] * money[i];}
for(int i = ; i >= ; --i){scanf("%d", &b[i]); sb += b[i] * money[i]; eb += b[i] * money[i];}
for(int i = ; i >= ; --i){scanf("%d", &c[i]); sc += c[i] * money[i]; ec += c[i] * money[i];} sum = sa + sb + sc;
if(ea < || eb < || ec < ){printf("impossible\n"); return ;} int now = ;
memset(dp, 0x7F, sizeof dp);
dp[now][sa][sb] = ;
for(int i = ; i < ; ++i)
{
for(int j = ; j < maxn; ++j) for(int k = ; k < maxn; ++k) dp[now ^ ][j][k] = dp[now][j][k];
int x = , y = ;
while((ea - x) % gcd[i]) ++x;
while((eb - y) % gcd[i]) ++y;
if((ec - (sum - x - y)) % gcd[i]) continue;
for(int j = x; j < maxn; j += gcd[i])
{
for(int k = y; k < maxn; k += gcd[i])
{
int vc = sum - j - k;
if(vc < ) break;
if(dp[now][j][k] == INF) continue;
for(int r = ; r <= a[i]; ++r) if(j >= money[i] * r)
for(int l = ; l <= r; ++l)
if(k + money[i] * l < maxn && vc + money[i] * (r - l) < maxn)
denew(dp[now ^ ][j - money[i] * r][k + money[i] * l], dp[now][j][k] + r); for(int r = ; r <= b[i]; ++r) if(k >= money[i] * r)
for(int l = ; l <= r; ++l)
if(j + money[i] * l < maxn && vc + money[i] * (r - l) < maxn)
denew(dp[now ^ ][j + money[i] * l][k - money[i] * r], dp[now][j][k] + r); for(int r = ; r <= c[i]; ++r) if(vc >= money[i] * r)
for(int l = ; l <= r; ++l)
if(j + money[i] * l < maxn && k + money[i] * (r - l) < maxn)
denew(dp[now ^ ][j + money[i] * l][k + money[i] * (r - l)], dp[now][j][k] + r); for(int r = ; r <= a[i]; ++r) if(j >= money[i] * r)
for(int l = ; l <= b[i]; ++l)
if(k >= money[i] * l && vc + money[i] * (r + l) < maxn)
denew(dp[now ^ ][j - money[i] * r][k - money[i] * l], dp[now][j][k] + l + r); for(int r = ; r <= a[i]; ++r) if(j >= money[i] * r)
for(int l = ; l <= c[i]; ++l)
if(vc >= money[i] * l && k + money[i] * (l + r) < maxn)
denew(dp[now ^ ][j - money[i] * r][k + money[i] * (l + r)], dp[now][j][k] + l + r); for(int r = ; r <= b[i]; ++r) if(k >= money[i] * r)
for(int l = ; l <= c[i]; ++l)
if(vc >= money[i] * l && j + money[i] * (l + r) < maxn)
denew(dp[now ^ ][j + money[i] * (l + r)][k - money[i] * r], dp[now][j][k] + l + r);
}
}
now ^= ; } if(dp[now][ea][eb] == INF) printf("impossible\n");
else printf("%d\n", dp[now][ea][eb]);
return ;
}

debt

BZOJ1022 小约翰的游戏:

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

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

数学归纳法就能证明了

 //date 20140626
#include <cstdio>
#include <cstring> const int maxn = ; int t, n;
int st[maxn]; int main()
{
scanf("%d", &t);
for(int tc = ; tc <= t; ++tc)
{
scanf("%d", &n);
int x, type = , sum = ;
for(int i = ; i <= n; ++i) {scanf("%d", &x); if(x > ) type = ; sum ^= x;}
sum = sum > ;
if(type ^ sum) printf("Brother\n"); else printf("John\n");
} return ;
}

John

BZOJ1023 仙人掌图:

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

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

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

 //date 20140627
#include <cstdio>
#include <cstring>
#include <vector> using namespace std;
const int maxn = ;
const int maxm = maxn << ; inline int getint()
{
int ans(); char w = getchar();
while(w < '' || '' < w) w = getchar();
while('' <= w && w <= '') {ans = ans * + w - ''; w = getchar();}
return ans;
} inline int min(int a, int b){return a < b ? a : b;}
inline int denew(int &a, int b){if(a > b){a = b; return ;} return ;}
inline int innew(int &a, int b){if(a < b){a = b; return ;} return ;} int n, m;
struct edge {int v, next;}E1[maxm], E2[maxm];
int a1[maxn], a2[maxn], nedge1, nedge2;
inline void add1(int u, int v){E1[++nedge1].v = v; E1[nedge1].next = a1[u]; a1[u] = nedge1;}
inline void add2(int u, int v){E2[++nedge2].v = v; E2[nedge2].next = a2[u]; a2[u] = nedge2;}
int dfn[maxn], low[maxn], ncol, timer, dpt[maxn], p[maxn], q[maxn], ind[maxn];
int dp1[maxn], dp2[maxn], dq[maxn], maxval[maxn], cmaxval[maxn];
vector<int> cyc[maxn], tmp; inline void tarjan(int v0)
{
static int d[maxn], now[maxn], stack[maxn], ins[maxn], stop, last, i, j;
memcpy(now, a1, sizeof a1);
for(dfn[v0] = low[v0] = timer += ins[d[last = ] = stack[stop = ] = v0] = ; last; )
{
if(!(j = now[i = d[last]]))
{
if(--last)
{
if(dfn[d[last]] == low[i])
{
for(++ncol; ins[i]; ins[stack[stop--]] = )
{
add2(n + ncol, stack[stop]); add2(stack[stop], n + ncol);
}
add2(n + ncol, d[last]); add2(d[last], n + ncol);
}
denew(low[d[last]], low[i]);
}
continue;
}
if(!dfn[E1[j].v])
{
ins[d[++last] = stack[++stop] = E1[j].v] = ; low[E1[j].v] = dfn[E1[j].v] = ++timer;
}else denew(low[i], dfn[E1[j].v]);
now[i] = E1[j].next;
}
} inline void DFS1(int v0)
{
static int d[maxn], now[maxn], last, i, j;
memcpy(now, a2, sizeof a2);
for(dpt[d[last = ] = v0] = ; last; )
{
if(!(j = now[i = d[last]])){--last; continue;}
if(!dpt[E2[j].v])
{
dpt[E2[j].v] = dpt[p[d[++last] = E2[j].v] = i] + ;
if(E2[j].v > n)
{
for(int w = a2[E2[j].v], sgn = ; w; w = E2[w].next)
{
if(E2[w].v == i) sgn = ;
if(sgn) cyc[E2[j].v].push_back(E2[w].v);
}
for(int w = a2[E2[j].v]; w; w = E2[w].next)
{
if(E2[w].v == i) break;
cyc[E2[j].v].push_back(E2[w].v);
}
}
++ind[i];
}
now[i] = E2[j].next;
}
} inline void BFS1()
{
int st = , ed = ;
for(int i = ; i <= n + ncol; ++i) if(!ind[i]) q[++ed] = i;
while(st < ed)
{
int x = q[++st];
if(!(--ind[p[x]])) q[++ed] = p[x];
}
} inline void dynamic_programming1()
{
for(int t = , i = q[]; t <= n + ncol; i = q[++t]) if(i > n)
{
for(int j = ; j < cyc[i].size(); ++j) innew(dp1[i], dp1[cyc[i][j]] + min(j, cyc[i].size() - j));
innew(dp1[p[i]], dp1[i]);
}
} inline void dynamic_programming2()
{
dp2[] = ;
int st, ed;
for(int t = n + ncol, i = q[n + ncol]; t; i = q[--t])
{
if(i > n)
{
if(dp1[i] == maxval[p[i]]) innew(dp2[i], cmaxval[p[i]]);
else innew(dp2[i], maxval[p[i]]); tmp.clear();
int tsz = cyc[i].size(), htsz = tsz >> ;
for(int w = ; w <= ; ++w)
{
tmp.push_back(dp2[i] - (w - ) * tsz);
for(int j = ; j < tsz; ++j) tmp.push_back(dp1[cyc[i][j]] - j - (w - ) * tsz);
}
st = ed = ;
for(int j = tsz - htsz + ; j <= tsz; ++j)
{
while(st < ed && tmp[dq[ed]] < tmp[j]) --ed;
dq[++ed] = j;
}
for(int j = tsz + ; j < (tsz << ); ++j)
{
while(st < ed && dq[st + ] < j - htsz) ++st;
if(st < ed) innew(dp2[cyc[i][j - tsz]], j + tmp[dq[st + ]]);
while(st < ed && tmp[dq[ed]] < tmp[j]) --ed;
dq[++ed] = j;
} tmp.clear();
for(int w = ; w <= ; ++w)
{
tmp.push_back(dp2[i] + (w - ) * tsz);
for(int j = ; j < tsz; ++j) tmp.push_back(dp1[cyc[i][j]] + j + (w - ) * tsz);
}
st = ed = ;
for(int j = (tsz << ) + htsz - ; j >= (tsz << ); --j)
{
while(st < ed && tmp[dq[ed]] < tmp[j]) --ed;
dq[++ed] = j;
}
for(int j = (tsz << ) - ; j > tsz; --j)
{
while(st < ed && dq[st + ] > j + htsz) ++st;
if(st < ed) innew(dp2[cyc[i][j - tsz]], -j + tmp[dq[st + ]]);
while(st < ed && tmp[dq[ed]] < tmp[j]) --ed;
dq[++ed] = j;
}
}else{
maxval[i] = dp2[i];
for(int j = a2[i]; j; j = E2[j].next) if(E2[j].v != p[i])
{
if(dp1[E2[j].v] > maxval[i])
{
cmaxval[i] = maxval[i];
maxval[i] = dp1[E2[j].v];
}else innew(cmaxval[i], dp1[E2[j].v]);
}
}
}
} int main()
{
n = getint(); m = getint();
for(int i = ; i <= m; ++i)
{
int x = getint(), last = getint(), now;
for(int i = ; i < x; ++i)
{
now = getint();
add1(now, last); add1(last, now);
last = now;
}
} tarjan(); DFS1(); BFS1();
dynamic_programming1();
dynamic_programming2(); int ans = ;
for(int i = ; i <= ncol; ++i) innew(ans, dp2[i + n] + dp1[i + n]);
printf("%d\n", ans);
return ;
}

cactus

SHOI2008 题目总结的更多相关文章

  1. bzoj1019 [SHOI2008]汉诺塔

    1019: [SHOI2008]汉诺塔 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1030  Solved: 638[Submit][Status] ...

  2. 【BZOJ】【1023】【SHOI2008】cactus仙人掌图

    DP+单调队列/仙人掌 题解:http://hzwer.com/4645.html->http://z55250825.blog.163.com/blog/static/150230809201 ...

  3. 1022: [SHOI2008]小约翰的游戏John【Nim博弈,新生必做的水题】

    1022: [SHOI2008]小约翰的游戏John Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 2709  Solved: 1726[Submit] ...

  4. bzoj1019 / P4285 [SHOI2008]汉诺塔

    P4285 [SHOI2008]汉诺塔 递推 题目给出了优先级,那么走法是唯一的. 我们用$0,1,2$代表$A,B,C$三个柱子 设$g[i][x]$为第$x$根柱子上的$i$个盘子,经过演变后最终 ...

  5. 【BZOJ1021】[SHOI2008]循环的债务(动态规划)

    [BZOJ1021][SHOI2008]循环的债务(动态规划) 题面 BZOJ 洛谷 题解 感觉以前的题目都好小清新啊,我这种智商丢失的选手完全写不动. 这题看着就像一个\(dp\),并且我们发现每种 ...

  6. 洛谷 P4244 [SHOI2008]仙人掌图 II 解题报告

    P4244 [SHOI2008]仙人掌图 II 题目背景 题目这个II是和SHOI2006的仙人掌图区分的,bzoj没有. 但是实际上还是和bzoj1023是一个题目的. 题目描述 如果某个无向连通图 ...

  7. 洛谷 P4279 [SHOI2008]小约翰的游戏 解题报告

    P4279 [SHOI2008]小约翰的游戏 题目描述 小约翰经常和他的哥哥玩一个非常有趣的游戏:桌子上有\(n\)堆石子,小约翰和他的哥哥轮流取石子,每个人取的时候,可以随意选择一堆石子,在这堆石子 ...

  8. bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

    http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] ...

  9. [bzoj1018][SHOI2008]堵塞的交通traffic_线段树

    bzoj-1018 SHOI-2008 堵塞的交通traffic 参考博客:https://www.cnblogs.com/MashiroSky/p/5973686.html 题目大意:有一天,由于某 ...

随机推荐

  1. 7 linux服务器程序规范

    1. Linux服务器程序一般以后台进程形式运行.后台进程又称守护进程(daemon),它没有控制终端,因而不会意外接收到用户输入.父进程通常为init(PID为1的进程)2. Linux服务器程序常 ...

  2. AngularJs学习笔记--directive

    原版地址:http://code.angularjs.org/1.0.2/docs/guide/directive Directive是教HTML玩一些新把戏的途径.在DOM编译期间,directiv ...

  3. 零成本实现WEB性能测试(一)性能测试基础

    1.1 初识性能测试 概念:负载测试&压力测试. 目的:评估系统的能力,识别系统弱点,系统调优,检测问题,验证稳定性. 分类:负载测试,压力测试,容量测试 B/S指标: Avg Rps,平均每 ...

  4. Virtualbox网络设置和无UI启动

    因工作需要,在Macbook上安装Ubuntu 14.04.2虚拟机,需要ssh连接操作. 一番查找资料,实践后可以正常工作了,记录一些信息以备用 无UI启动虚拟机,可使用以下命令: VBoxMana ...

  5. HDOJ 1050 Moving Tables

    Moving Tables Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  6. php随机数怎么获取?一个简单的函数就能生成

    小美女建了一个站,有些页面相似度比较高,想添加一些字段来实现差异化,比如用php随机数生成从10到100之间随机一个数字.其实会php的朋友几十个字符就能实现了,如下代码所示,简单吧?10代表最小值, ...

  7. C# WinForm窗口最小化到系统托盘

    * C# WinForm窗口最小化到系统托盘http://hi.baidu.com/kfxtgtqyapouyze/item/8ccfdcd5a174a7312a35c7c3 主要功能:(1).程序启 ...

  8. URAL 1183 Brackets Sequence(DP)

    题目链接 题意 : 给你一串由括号组成的串,让你添加最少的括号使该串匹配. 思路 : 黑书上的DP.dp[i][j] = min{dp[i+1][j-1] (sh[i] == sh[j]),dp[i] ...

  9. lightoj 1397 - Sudoku Solver

    思路:每次找出可能情况最少的位置枚举可能情况!!! poj2676和这题一样不过poj数据比较水,很容易过. 代码如下: #include<iostream> #include<cs ...

  10. hdu 3886 Final Kichiku “Lanlanshu” 数位DP

    思路: dp[i][j][k]:满足在字符串的j位,前一位数字是k. 代码如下: #include<iostream> #include<cstdio> #include< ...