BZOJ3598 [Scoi2014]方伯伯的商场之旅

题目名字好长。

 

当年在考场上只会分块打表的题。

 

前几天有人讲之后觉得大概可做。而且好像那个方法很优秀。

 

于是我难得地去写了一回不用记搜且不记上界的数位dp。感觉这个玩意写出来很短,但是思考和调试的复杂度变高了。我觉得是状态定义变得不明确的原因。也许再补一些题就好了。

 

考虑先让所有都合并到最低位,一个基础数位dp。(然后写丑了好久)

 

然后考虑如果把一个数字的集合位置从p移动到p+1,设这个数字从低位到高位是a[0]..a[n-1],那么贡献就是∑a[0..p]-∑a[p+1..n-1]。显然当p从右往左移动的时候它是会先负后正的。如果它负的话就减,否则就不玩了。可以发现它们互相不影响。于是对于每一个p单独计算一遍贡献就行了。

 

然后还是没有原版跑得快。OTL。

 

#define PROC "shop"
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long dint;
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define _l (long long int)

const int maxn = 53;
const int maxm = 509;
const int mb = 253;

dint l, r;
int n, m, bs, a[maxn];

dint dpBeginning() {
	static dint f[maxn], c[maxn], g[maxn], cr[maxn];
	c[0] = bs;
	f[0] = 0;
	for (int i = 1; i < n; ++ i) {
		c[i] = c[i - 1] * bs;
		f[i] = f[i - 1] * bs;
		for (int j = 1; j < bs; ++ j)
			f[i] = f[i] + c[i - 1] * i * j;
	}
	cr[0] = g[0] = 0;
	cr[1] = a[0] + 1;
	g[1] = a[1] * cr[1];
	for (int i = 2; i <= n; ++ i)  {
		cr[i] = g[i] = 0;
		for (int d = 0; d < a[i - 1]; ++ d) {
			cr[i] += c[i - 2];
			g[i] += f[i - 2] + c[i - 2] * (d * (i - 1) + a[i] * i);
		}
		cr[i] += cr[i - 1];
		g[i] += cr[i - 1] * a[i] * i + g[i - 1];
	}
	return g[n];
}

dint dp(dint v) {
	static dint c[maxn][maxm];
	static dint cr[maxn][maxm];
	m = 0;
	for (n = 0; v; v /= bs)
		a[n ++] = v % bs;
	a[n] = 0;
	m = n * bs;
	dint ans(dpBeginning());
	//printf(lld "\n", ans);
	for (int p = 1; p < n; ++ p) {
		memset(c, 0, sizeof(c));
		for (int i = 0; i < bs; ++ i)
			c[0][mb - i] = 1;
		for (int i = 1; i < n; ++ i)
			for (int j = mb - m; j <= mb + m; ++ j)
				if (c[i - 1][j])
					for (int d = 0; d < bs; ++ d) {
						int t((i < p) ? j - d : j + d);
						c[i][t] += c[i - 1][j];
					}
		memset(cr, 0, sizeof(cr));
		cr[0][mb - a[0]] = 1;
		for (int i = 1; i <= n; ++ i) {
			for (int d = 0; d < a[i - 1]; ++ d)
				if (i > 1) {
					for (int j = mb - m; j <= mb + m; ++ j)
						if (c[i - 2][j]) {
							int t((i - 1 < p) ? j - d : j + d);
							t = (i < p) ? t - a[i] : t + a[i];
							cr[i][t] += c[i - 2][j];
						}
				}
				else  {
					int t((i < p) ? mb - d - a[i] : mb - d + a[i]);
					++ cr[i][t];
				}
			for (int j = mb - m; j <= mb + m; ++ j)
				if (cr[i - 1][j]) {
					int t((i < p) ? j - a[i] : j + a[i]);
					cr[i][t] += cr[i - 1][j];
				}
		}
		for (int i = 1; i <= m; ++ i)
			ans -= cr[n][i + mb] * i;
	}
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen(PROC ".in", "r", stdin);
	freopen(PROC ".out", "w", stdout);
#endif

	scanf(lld lld "%d", &l, &r, &bs);
	printf(lld "\n", dp(r) - dp(l - 1));
}

BZOJ1604 [Usaco2008 Open]Cow Neighborhoods 奶牛的邻居

在一道usaco的题上wa了三次。我也是厉害。

 

其实好像有简单做法的。不过我是要脑补一下曼哈顿最小生成树。

 

对于一个平面图上的点的曼哈顿最小生成树,一定存在一种方案,使得每个点的度数至多为8。也就是说以斜率为0,inf,1,-1的四条直线分出来的八个区域里各有一个点就行了。然后是可以保证的。

 

于是就是拿俩树状数组扫四遍就行了。

 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

using namespace std;

const int inf = 0x3f3f3f3f;

struct point {
	int x, y, i;
	point() {
		x = -inf, y = -1;
	}
	point(int xo, int yo) {
		x = xo, y = yo;
	}
	inline void init(int ix) {
		scanf("%d%d", &x, &y);
		i = ix;
	}
};

inline bool operator <(const point& a, const point& b) {
	return (a. x < b. x) || (a. x == b. x && a. y < b. y);
}

const int maxn = 100009;
const int maxx = 1e9;

int n, c, r[maxn], sz[maxn], cnt, msz;
int v[maxn], tv, w[maxn], tw;
point a[maxn], ta[maxn], tb[maxn];

int getRoot(int x) {
	register int p, q;
	for (p = r[x]; p ^ r[p]; p = r[p]);
	for (; x ^ p; q = r[x], r[x] = p, x = q);
	return x;
}
inline void join(int u, int v) {
	r[getRoot(u)] = getRoot(v);
}

point btQry(point* t, int p) {
	point s(-inf, -1);
	for (++ p; p; p -= (p & -p))
		if (s < t[p])
			s = t[p];
	return s;
}
void btChg(point* t, int p, point v) {
	for (++ p; p < maxn; p += (p & -p))
		if (t[p] < v)
			t[p] = v;
}

inline bool isNeighbour(const point& a, const point& b) {
	return abs(a. x - b. x) + abs(a. y - b. y) <= c;
}

void span() {
	sort(a, a + n);
	tv = tw;
	for (int i = 0; i < n; ++ i) {
		v[i] = a[i]. y - a[i]. x;
		w[i] = - a[i]. x - a[i]. y;
	}
	sort(v, v + n);
	tv = unique(v, v + n) - v;
	sort(w, w + n);
	tw = unique(w, w + n) - w;
	for (int i = 1; i <= tv || i <= tw; ++ i)
		ta[i] = tb[i] = point();
	for (int i = 0; i < n; ++ i) {
		int p(lower_bound(v, v + tv, a[i]. y - a[i]. x) - v);
		point g(btQry(ta, p));
		if (g. y > -1 && isNeighbour(a[i], a[g. y]))
		   join(a[i]. i, a[g. y]. i);	
		btChg(ta, p, point(a[i]. x + a[i]. y, i));
		p = lower_bound(w, w + tw, - a[i]. x - a[i]. y) - w;
		g = btQry(tb, p);
		if (g. y > -1 && isNeighbour(a[i], a[g. y]))
		   join(a[i]. i, a[g. y]. i);	
		btChg(tb, p, point(a[i]. x - a[i]. y, i));
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &c);
	for (int i = 0; i < n; ++ i) {
		a[i]. init(i);
		r[i] = i;
	}
	span();
	for (int i = 0; i < n; ++ i)
		a[i]. x = maxx - a[i]. x;
	span();
	for (int i = 0; i < n; ++ i)
		swap(a[i]. x, a[i]. y);
	span();
	for (int i = 0; i < n; ++ i)
		a[i]. x = maxx - a[i]. x;
	span();
	memset(sz, 0, sizeof(sz));
	for (int i = 0; i < n; ++ i) {
		if (r[i] == i)
			++ cnt;
		++ sz[getRoot(i)];
	}
	for (int i = 0; i < n; ++ i)
		if (r[i] == i && sz[i] > msz)
			msz = sz[i];
	printf("%d %d\n", cnt, msz);
}

BZOJ1814 Ural 1519 Formula 1

插头dp的练手题么。虽然之前还没写过哈密顿回路的题。

 

学习了一下广义括号法。发现状态数比之前记连通性的方法要少。仔细想想发现,两个连通的插头的顺序有关。不能存在1212这种东西。所以连通性的方法记了很多废状态。那把连通性方法改进一下会不会变得更快呢?可以研究一下。我觉得应该是等效了吧。

 

今天的debug时间明显缩短了。虽然还是debug了老半天。然后怎么代码还是那么长。想起我每次都觉得把东西挤到一起看起来很不雅观,于是去写一堆找转移的函数。晕。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long dint;
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define _l (long long int)

const int maxn = 15;
const int maxst = 50009;

#define mbit(_x_,_y_) ((_x_)<<((_y_)<<1))
#define gbit(_x_,_y_) (((_x_)>>((_y_)<<1))&3)
#define cbit(_x_,_y_) ((_x_)&(~(3<<((_y_)<<1))))

int n, m, tots, slst[maxst];
bool mp[maxn][maxn];
dint f[2][maxst];

void dfsState(int l, int c, int z) {
	if (l == m + 1) {
		if (!c)
			slst[tots ++] = z;
	}
	else {
		dfsState(l + 1, c, z);
		dfsState(l + 1, c + 1, z | mbit(1, l));
		if (c)
			dfsState(l + 1, c - 1, z | mbit(2, l));
	}
}
void preState() {
	tots = 0;
	dfsState(0, 0, 0);
	sort(slst, slst + tots);
}
inline int fState(int s) {
	register int ret(lower_bound(slst, slst + tots, s) - slst);
	if (slst[ret] != s)
		puts("naive");
	return ret;
}

#define inc(_a_,_b_) (_a_+=_b_)
inline int brv(int x) {
	if (!x)
		return 0;
	else
		return (x == 1) ? 1 : -1;
}

bool isEnd(int s, int y) {
	for (int i = 0; i < y; ++ i)
		if (gbit(s, i))
			return 0;
	for (int i = y + 2; i <= m; ++ i)
		if (gbit(s, i))
			return 0;
	return 1;
}
inline int mergePlug(int s, int y) {
	int p, c;
	if (gbit(s, y) != gbit(s, y + 1)) {
		s = cbit(s, y);
		s = cbit(s, y + 1);
		return fState(s);
	}
	else if (gbit(s, y) == 1) {
		for (c = 1, p = y + 1; c; ++ p, c += brv(gbit(s, p)));
		s = cbit(s, y);
		s = cbit(s, y + 1);
		s = cbit(s, p);
		return fState(s | mbit(1, p));
	}
	else {
		for (c = -1, p = y; c; -- p, c += brv(gbit(s, p)));
		s = cbit(s, y);
		s = cbit(s, y + 1);
		s = cbit(s, p);
		return fState(s | mbit(2, p));
	}
}
inline int extPlugRight(int s, int y) {
	s |= mbit(gbit(s, y), y + 1);
	s = cbit(s, y);
	return fState(s);
}
inline int extPlugDown(int s, int y) {
	s |= mbit(gbit(s, y + 1), y);
	s = cbit(s, y + 1);
	return fState(s);
}
inline int forkPlug(int s, int y) {
	s |= mbit(1, y);
	s |= mbit(2, y + 1);
	return fState(s);
}
inline int nextRow(int s) {
	int r(0);
	for (int i = 0; i < m; ++ i)
		r |= mbit(gbit(s, i), i + 1);
	return fState(r);
}

dint dp() {
	int cur(0), prv(1);
	dint ans;
	memset(f, 0, sizeof(f));
	f[cur][0] = 1;
	for (int x = 0; x < n; ++ x) {
		for (int y = 0; y < m; ++ y) {
			if (mp[x][y])
				ans = 0;
			swap(cur, prv);
			memset(f[cur], 0, sizeof(f[cur]));
			for (int i = 0; i < tots; ++ i)
				if (f[prv][i]) {
					int s(slst[i]);;
					if (mp[x][y]) {
						if (gbit(s, y) && gbit(s, y + 1)) {
							if (gbit(s, y) == 1 && gbit(s, y + 1) == 2) {
								if (isEnd(s, y))
									inc(ans, f[prv][i]);
							}
							else
								inc(f[cur][mergePlug(s, y)], f[prv][i]);
						}
						else if (gbit(s, y)) {
							inc(f[cur][i], f[prv][i]);
							inc(f[cur][extPlugRight(s, y)], f[prv][i]);
						}
						else if (gbit(s, y + 1)) {
							inc(f[cur][i], f[prv][i]);
							inc(f[cur][extPlugDown(s, y)], f[prv][i]);
						}
						else
							inc(f[cur][forkPlug(s, y)], f[prv][i]);
					}
					else {
						if (!gbit(s, y) && !gbit(s, y + 1))
							inc(f[cur][i], f[prv][i]);
					}
				}
		}
		swap(cur, prv);
		memset(f[cur], 0, sizeof(f[cur]));
		for (int i = 0; i < tots; ++ i)
			if (f[prv][i] && !gbit(slst[i], m))
				inc(f[cur][nextRow(slst[i])], f[prv][i]);
	}
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; ++ i) {
		char g[maxn];
		scanf("%s", g);
		for (int j = 0; j < m; ++ j)
			mp[i][j] = (g[j] == '.');
	}
	preState();
	printf(lld "\n", dp());
}

BZOJ2402 陶陶的难题II

orz mhy。看mhy的刷题记录成为了我成天的刷题方向了。

 

其实最初也没有想清楚。然后看了一眼mhy写的题解写了凸壳,秒懂。

 

二分一个答案,设它是ans。如果ans≥realAns的话,那么就会存在yi-ans*xi+qj-ans*pj≥0。然后i和j就分开了。于是变成了求一条路径上yi-ans*xi的最大值。把xi看作k,把yi看作b,就变成半平面交了。于是开归并式线段树把凸壳给记录下来就好了。然后复杂度大约是单次询问O(lg3(n))的?链剖后每棵树单独建树,然后复杂度我就不会算了TT。

 

于是就硬着头皮写了,然后发现跑得还挺快。

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 30009;
const int maxl = 15;
const int max_buf = maxn * maxl * 2;
const double eps = 1e-6;
const double finf = 1e10;

int ibuf_arr[max_buf], *ibuf(ibuf_arr);
double fbuf_arr[max_buf], *fbuf(fbuf_arr);

struct edge {
	int t;
	edge *next;
};

struct line {
	double k, b;
	inline double xCross(const line& a) {
		return (a. b - b) / (k - a. k);
	}
	inline double f(double x) {
		return k * x + b;
	}
};
line a[maxn << 1];

struct hull {
	int t;
	int *l;
	double *cx;
	void init(int x) {
		t = 1;
		l = ibuf ++;
		l[0] = x;
		cx = 0;
	}
	void merge(const hull& x, const hull& y) {
		static int stc[maxn];
		static double tx[maxn];
		t = 0;
		for (int i = 0, j = 0, c; i < x. t || j < y. t; ) {
			if (i == x. t || (j < y. t && a[x. l[i]]. k > a[y. l[j]]. k))
				c = y. l[j ++];
			else 
				c = x. l[i ++];
			if (t && fabs(a[c]. k - a[stc[t - 1]]. k) < eps) {
				if (a[c]. b >= a[stc[t - 1]]. b)
					-- t;
				else
					continue;
			}
			while (t > 1 && a[c]. xCross(a[stc[t - 2]]) <= tx[t - 2])
				-- t;
			if (t)
				tx[t - 1] = a[c]. xCross(a[stc[t - 1]]);
			stc[t ++] = c;
		}
		l = ibuf;
		ibuf += t;
		cx = fbuf;
		fbuf += t - 1;
		for (int i = 0; i < t; ++ i) {
			l[i] = stc[i];
			if (i < t - 1)
				cx[i] = a[stc[i]]. xCross(a[stc[i + 1]]);
		}
	}
	double qry(double x) {
		int p(lower_bound(cx, cx + t - 1, x) - cx);
		return a[l[p]]. f(x);
	}
};
struct seg {
	hull h[2];
	int l, r;
	seg *ls, *rs;
};

int n, m;
double qans[2], qx;
int fa[maxn], d[maxn], sz[maxn], tc, fc[maxn], ch[maxn], cl[maxn], *ci[maxn];
edge elst[maxn << 1], *ep(elst), *head[maxn];
seg slst[maxn << 2], *sp(slst), *rt[maxn];

inline void upMax(double& a, double b) {
	if (a < b)
		a = b;
}
inline void addEdge(int u, int v) {
	ep-> t = v;
	ep-> next = head[u];
	head[u] = ep ++;
}

#define cpos(_p_) (d[_p_]-d[ch[fc[_p_]]])
#define midp(p) ((p->l+p->r)>>1)

inline seg* sgtBuild(int l, int r, int* ci) {
	seg *p(sp ++);
	p-> l = l;
	p-> r = r;
	if (l + 1 == r) {
		p-> h[0]. init(ci[l]);
		p-> h[1]. init(ci[l] + n);
	}
	else {
		p-> ls = sgtBuild(l, midp(p), ci);
		p-> rs = sgtBuild(midp(p), r, ci);
		p-> h[0]. merge(p-> ls-> h[0], p-> rs-> h[0]);
		p-> h[1]. merge(p-> ls-> h[1], p-> rs-> h[1]);
	}
	return p;
}
inline void sgtQry(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r) {
		upMax(qans[0], p-> h[0]. qry(qx));
		upMax(qans[1], p-> h[1]. qry(qx));
	}
	else if (r <= midp(p))
		sgtQry(p-> ls, l, r);
	else if (l >= midp(p))
		sgtQry(p-> rs, l, r);
	else {
		sgtQry(p-> ls, l, midp(p));
		sgtQry(p-> rs, midp(p), r);
	}
}

void divide() {
	static int q[maxn];
	int hd(0), tl(1);
	memset(d, 0, sizeof(d));
	d[q[0] = 1] = 1;
	fa[1] = 0;
	while (hd < tl) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (!d[e-> t]) {
				d[e-> t] = d[p] + 1;
				fa[e-> t] = p;
				q[tl ++] = e-> t;
			}
	}
	tc = 0;
	for (int i = tl - 1; i >= 0; -- i) {
		int p(q[i]), z(-1);
		sz[p] = 1;
		for (edge* e = head[p]; e; e = e-> next)
			if (fa[e-> t] == p) {
				sz[p] += sz[e-> t];
				if (z == -1 || sz[e-> t] > sz[z])
					z = e-> t;
			}
		if (z == -1)
			cl[fc[p] = ++ tc] = 1;
		else
			++ cl[fc[p] = fc[z]];
		ch[fc[p]] = p;
	}
	for (int i = 1; i <= tc; ++ i) {
		ci[i] = ibuf;
		ibuf += cl[i];
	}
	for (int i = 1; i <= n; ++ i)
		ci[fc[i]][cpos(i)] = i;
	for (int i = 1; i <= tc; ++ i)
		rt[i] = sgtBuild(0, cl[i], ci[i]);
}

bool check(int u, int v, double x) {
	qans[0] = qans[1] = -finf;
	qx = -x;
	while (fc[u] ^ fc[v])
		if (d[ch[fc[u]]] > d[ch[fc[v]]]) {
			sgtQry(rt[fc[u]], 0, cpos(u) + 1);
			u = fa[ch[fc[u]]];
		}
		else {
			sgtQry(rt[fc[v]], 0, cpos(v) + 1);
			v = fa[ch[fc[v]]];
		}
	if (d[u] < d[v])
		sgtQry(rt[fc[u]], cpos(u), cpos(v) + 1);
	else
		sgtQry(rt[fc[u]], cpos(v), cpos(u) + 1);
	return qans[0] + qans[1] >= 0;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i)
		scanf("%lf", &a[i]. k);
	for (int i = 1; i <= n; ++ i)
		scanf("%lf", &a[i]. b);
	for (int i = 1; i <= n; ++ i)
		scanf("%lf", &a[i + n]. k);
	for (int i = 1; i <= n; ++ i)
		scanf("%lf", &a[i + n]. b);
	memset(head, 0, sizeof(head));
	for (int i = 1; i < n; ++ i) {
		int u, v;
		scanf("%d%d", &u, &v);
		addEdge(u, v);
		addEdge(v, u);
	}
	divide();
	scanf("%d", &m);
	while (m --) {
		int u, v;
		scanf("%d%d", &u, &v);
		double l(1e-8), r(1e8);
		while (l + eps < r) {
			double mid((l + r + eps) / 2.0);
			if (check(u, v, mid))
				l = mid;
			else
				r = mid - eps;
		}
		printf("%.4lf\n", l);
	}
}

BZOJ1513 [POI2006]Tet-Tetris 3D

这么水的题居然还要写题解。要不要说我写了半个下午+半个晚上。

 

丧心病狂想写一发zkw去抢速度rank。然后发现zkw怎么支持区间修改查询TT。课件根本没讲懂。而且max怎么区间加。

 

想了很久终于想出了来了。然后交上去发现常数巨大比普通线段树还要慢TT。到头来想想如果我直接写普通线段树说不定十多分钟就能过,而且代码量还会小一些。因为那个是可以直接通用的,但是这个比较麻烦。

 

其实写这个就题解就是想记录一下我yy出来的zkw区间修改区间查询的方法。

 

这道题是区间查询最大值+区间修改。如果是写一般的线段树,需要记录两个值a和v(至于为啥我用了这俩神奇的字母我也不知道),分别表示当前区间被完整覆盖的修改的最大值(又称lazy标记)和当前区间的最大值。然后所以zkw也要记这两个东西。

 

如果我们把zkw看作一棵树,那么每次修改和询问操作的时候都是对一些线段的a和v进行操作。(废话)

 

首先考虑修改。zkw经典的变为开区间取中间能处理a值的改变,但是不能处理v值的改变。因为v值改变的线段不一定会被当前区间包含。然后仔细考虑发现a值没有变但是v值变了的点只可能存在于两个端点到根的路径上。那就直接强行更新一发就好了啊。然后更新的区域就变成了一个有点像倒置漏斗的形状。

 

再考虑询问。a要覆盖当前区间,v要被当前区间覆盖。那不就是修改把a和v的做法反一下么。

 

然后好像也不是很麻烦的样子哈。

 

然后常数很大哈。

 

那还拿zkw做何用。

 

晕。

 

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>

using namespace std;

const int buf_len = 4567;
char buf[buf_len], *bufb(buf), *bufe(buf + 1);
#define readBuf() { \
	if (++ bufb == bufe) \
		bufe = (bufb = buf) + fread(buf, 1, sizeof(buf), stdin); \
}
#define readInt(_x_) { \
	register int _s_(0); \
	do { \
		readBuf(); \
	} while (!isdigit(*bufb)); \
	do { \
		_s_ = _s_ * 10 + *bufb - 48; \
		readBuf(); \
	} while (isdigit(*bufb)); \
	_x_ = _s_; \
}

const int maxn = 2049;

int q, n, m, bn, bm;

inline void upMax(int& a, int b) {
	if (a < b)
		a = b;
}
#define upMax_d(_a_,_b_) { \
	if (_a_ < _b_) \
		_a_ = _b_; \
}

struct zkw_tree_y {
	int v[maxn], a[maxn];
	zkw_tree_y() {
		memset(v, 0, sizeof(v));
		memset(a, 0, sizeof(a));
	}
	int qry(int lo, int ro) {
		int s(0), l, r;
		for (l = lo + bm; l; l >>= 1)
			upMax_d(s, a[l]);
		for (r = ro + bm; r; r >>= 1)
			upMax_d(s, a[r]);
		for (l = lo + bm - 1, r = ro + bm + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
			if (!(l & 1))
				upMax_d(s, v[l ^ 1]);
			if (r & 1)
				upMax_d(s, v[r ^ 1]);
		}
		return s;
	}
	void chg(int lo, int ro, int h) {
		int l, r;
		for (l = lo + bm; l; l >>= 1)
			upMax_d(v[l], h);
		for (r = ro + bm; r; r >>= 1)
			upMax_d(v[r], h);
		for (l = lo + bm - 1, r += ro + bm + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
			if (!(l & 1)) {
				upMax_d(a[l ^ 1], h);
				upMax_d(v[l ^ 1], h);
			}
			if (r & 1) {
				upMax_d(a[r ^ 1], h);
				upMax_d(v[r ^ 1], h);
			}
		}
	}
};

zkw_tree_y v[maxn], a[maxn];

int zkwQryX(int lo, int ro, int yl, int yr) {
	int s(0), l, r;
	for (l = lo + bn; l; l >>= 1)
		upMax(s, a[l]. qry(yl, yr));
	for (r = ro + bn; r; r >>= 1)
		upMax(s, a[r]. qry(yl, yr));
	for (l = lo + bn - 1, r = ro + bn + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
		if (!(l & 1))
			upMax(s, v[l ^ 1]. qry(yl, yr));
		if (r & 1)
			upMax(s, v[r ^ 1]. qry(yl, yr));
	}
	return s;
}
void zkwChgX(int lo, int ro, int yl, int yr, int h) {
	int l, r;
	for (l = lo + bn; l; l >>= 1)
		v[l]. chg(yl, yr, h);
	for (r = ro + bn; r; r >>= 1)
		v[r]. chg(yl, yr, h);
	for (l = lo + bn - 1, r = ro + bn + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
		if (!(l & 1)) {
			a[l ^ 1]. chg(yl, yr, h);
			v[l ^ 1]. chg(yl, yr, h);
		}
		if (r & 1) {
			a[r ^ 1]. chg(yl, yr, h);
			v[r ^ 1]. chg(yl, yr, h);
		}
	}
}

int main() {
#ifndef ONLINE_JUDGE
//	freopen("in.txt", "r", stdin);
#endif

	readInt(n);
	readInt(m);
	readInt(q);
	memset(a, 0, sizeof(a));
	memset(v, 0, sizeof(v));
	for (bn = 1; bn < n + 2; bn <<= 1);
	for (bm = 1; bm < m + 2; bm <<= 1);
	while (q --) {
		int x, y, p, q, h;
		readInt(p);
		readInt(q);
		readInt(h);
		readInt(x);
		readInt(y);
		p += x ++, q += y ++;
		h += zkwQryX(x, p, y, q);
		zkwChgX(x, p, y, q, h);
	}
	printf("%d\n", zkwQryX(1, n, 1, m));
}

BZOJ2704 旅游

居然网上都没有找到程序来和我拍。好吧其实它是一道权限题而且过的人也不多。

 

其实就是裸的插头DP。本来想学一下广义括号的然后被状态数吓到了于是只好还是写最小表示。连通性。

 

然后我发现最小表示连通性很容易写挂。而在findstate的时候检查一下是个不错的办法。至少这道题靠这个就可以直接debug出来问题了。

 

比上次写插头要好许多了。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long dint;
#define _l (long long int)
#define mbit(_x_,_y_) ((_l _x_)<<((_y_)<<2))
#define gbit(_x_,_y_) (((_x_)>>((_y_)<<2))&0xf)

const int maxn = 13;
const int maxst = 570009;

dint slst[maxst];
int n, m, v[maxn][maxn], tots, f[2][maxst], cnt[17];
bool av[2][maxst];

void dfsState(int l, int tot, dint z) {
	if (l == m) {
		for (int i = 1; i <= tot; ++ i)
			if (cnt[i] != 2)
				return;
		slst[tots ++] = z;
	}
	else {
		for (int i = 0; i <= tot; ++ i)
			if (!i || cnt[i] == 1) {
				++ cnt[i];
				dfsState(l + 1, tot, z | mbit(i, l));
				-- cnt[i];
			}
		++ cnt[tot + 1];
		dfsState(l + 1, tot + 1, z | mbit(tot + 1, l));
		-- cnt[tot + 1];
		if (!cnt[15] && l < m - 1) {
			++ cnt[15];
			dfsState(l + 1, tot, z | mbit(15, l));
			-- cnt[15];
		}

	}
}

void preState() {
	tots = 0;
	memset(cnt, 0, sizeof(cnt));
	dfsState(0, 0, 0);
	sort(slst, slst + tots);
}

inline int fState(dint x) {
	return lower_bound(slst, slst + tots, x) - slst;
	//if (slst[ret] != x)
		//puts("naive");
	//return ret;
}

#define fUpMax(_sa_,_sv_) { \
	register int _a_(_sa_), _v_(_sv_); \
	if (!av[cur][_a_]) { \
		av[cur][_a_] = 1; \
		f[cur][_a_] = _v_; \
	} \
	else if (_v_ > f[cur][_a_]) \
		f[cur][_a_] = _v_; \
}
#define upMax(_a_,_b_) { \
	if (_a_ < _b_) \
		_a_ = _b_; \
}

void unzipState(dint z, int* a) {
	for (int i = 0; i < m; ++ i)
		a[i] = gbit(z, i);
}

dint zipState(int* a) {
	dint s(0);
	for (int i = 0; i < m; ++ i)
		s |= mbit(a[i], i);
	return s;
}

bool isFinalPlug(int *a, int y) {
	for (int i = 0; i < y - 1; ++ i)
		if (a[i])
			return 0;
	for (int i = y + 1; i <m; ++ i)
		if (a[i])
			return 0;
	return 1;
}

int mergePlug(int* a, int y) {
	static int b[maxn], c[17];
	memcpy(b, a, sizeof(b));
	if (a[y - 1] == 15) {
		b[y - 1] = b[y];
		b[y] = 0;
	}
	else {
		int f(a[y - 1]), t(a[y]);
		for (int i = 0; i < m; ++ i)
			if (b[i] == f)
				b[i] = t;
		b[y - 1] = b[y] = 0;
		memset(c, 0, sizeof(c));
		f = 0;
		for (int i = 0; i < m; ++ i)
			if (b[i]) {
				if (c[b[i]])
					b[i] = c[b[i]];
				else
					b[i] = c[b[i]] = ++ f;
			}
	}
	return fState(zipState(b));
}

int extendPlugRight(int* a, int y) {
	static int b[maxn];
	memcpy(b, a, sizeof(b));
	if (a[y - 1] == 15) {
		int t(1);
		for (int i = 0; i < y - 1; ++ i)
			if (a[i] < 15 && a[i] + 1 > t)
				t = a[i] + 1;
		for (int i = y + 1; i < m; ++ i)
			if (a[i] < 15 && a[i] >= t)
				++ b[i];
		b[y - 1] = b[y] = t;
	}
	else {
		b[y] = b[y - 1];
		b[y - 1] = 0;
	}
	return fState(zipState(b));
}

int newPlug(int* a, int y) {
	static int b[maxn];
	memcpy(b, a, sizeof(b));
	b[y] = 15;
	return fState(zipState(b));
}

int dp() {
	int prv(0), cur(1), so(fState(15)), ans(0);
	memset(av, 0, sizeof(av));
	av[cur][so] = 1;
	f[cur][so] = v[0][0];
	for (int x = 0; x < n; ++ x)
		for (int y = !x ? 1 : 0; y < m; ++ y) {
			swap(prv, cur);
			memset(av[cur], 0, sizeof(av[cur]));
			for (int i = 0; i < tots; ++ i)
				if (av[prv][i]) {
					dint s(slst[i]);
					static int a[maxn];
					unzipState(s, a);
					if (v[x][y]) {
						if (y && a[y - 1] && a[y]) {
							if (a[y - 1] == a[y]) {
								if (isFinalPlug(a, y))
									upMax(ans, f[prv][i] + v[x][y]);
							}
							else {
								fUpMax(mergePlug(a, y), f[prv][i] + v[x][y]);
							}
						}
						if (y && a[y - 1] && !a[y])
							fUpMax(extendPlugRight(a, y), f[prv][i] + v[x][y]);
						if (a[y] && (!y || a[y - 1] != 15)) 
							fUpMax(i, f[prv][i] + v[x][y]);
						if (!a[y] && (!y || a[y - 1] != 15) && y < m - 1)
							fUpMax(newPlug(a, y), f[prv][i] + v[x][y]);
					}
					if (!a[y] && (!y || a[y - 1] != 15))
						fUpMax(i, f[prv][i]);
				}
		}
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	bool sw(0);
	scanf("%d%d", &n, &m);
	if (n < m)
		sw = 1;
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < m; ++ j)
			if (sw)
				scanf("%d", v[j] + i);
			else
				scanf("%d", v[i] + j);
	if (sw)
		swap(n, m);
	preState();
	printf("%d\n", dp());
}

BZOJ2178 圆的面积并

计算几何题。别人都用simpson搞。然后我决定不水一发,用hwd神犇在wc上讲的关键点法做。

 

然后就搞到了12点半。发现坑数次。还专门用js写了个画圆的。当然我现在知道怎么画圆了。过两天去把oj7的统计图改一改。

 

思路是把所有圆的左右端点和所有圆的两两交的x提出来,unique一发(不然会tle显然的)。这样两个x之间的部分的圆弧是不会有交的。那么就可以视为一堆弓形和梯形了。然后扫描一下中位线的有效长度算梯形。每次新进入一个连通区域和出来的时候加相应弓形的面积。

 

需要注意按y排序的时候是按弓形与竖线的交点的y,而不是梯形的y,否则会有非常神奇的精度误差。另外算弓形面积必需要用反三角函数(至少我没研究出怎么不用)。如果用acos的话会暴精度暴得很惨。要用atan2来提高精度。

 

然后这样写连样例都会tle。仔细感受一下发现好像如果有一堆同心圆的话都会tle。然后考虑去掉包含的圆之后似乎就快了。然后就交了,然后居然跑过了。

 

然后来欣赏一下我的时间巨大,空间巨大,长度巨大。挤在一堆simpson里显得特别郁闷的代码。

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

const double eps = 1e-8;
const double pi = acos(-1);

inline int sgn(double x) {
	return (x > eps) - (x < -eps);
}
inline double sqr(double x) {
	return x * x;
}

typedef struct geo_obj {
	double x, y;
	geo_obj() {}
	geo_obj(double xo, double yo) {
		x = xo, y = yo;
	}
	inline void read() {
		scanf("%lf%lf", &x, &y);
	}
	inline geo_obj vertical() {
		return geo_obj(-y, x);
	}
	inline double len() {
		return sqrt(sqr(x) + sqr(y));
	}
} point, vect;
inline geo_obj operator +(const geo_obj& a, const geo_obj& b) {
	return geo_obj(a. x + b. x, a. y + b. y);
}
inline geo_obj operator -(const geo_obj& a, const geo_obj& b) {
	return geo_obj(a. x - b. x, a. y - b. y);
}
inline geo_obj operator *(const geo_obj& a, const double& b) {
	return geo_obj(a. x * b, a. y * b);
}
inline double operator *(const geo_obj& a, const geo_obj& b) {
	return a. x * b. y - a. y * b. x;
}
inline double operator &(const geo_obj& a, const geo_obj& b) {
	return a. x * b. x + a. y * b. y;
}
inline double sqDist(const geo_obj& a, const geo_obj& b) {
	return sqr(a. x - b. x) + sqr(a. y - b. y);
}

struct circle {
	point o;
	double r, sr;
	inline void read() {
		o. read();
		scanf("%lf", &r);
		sr = sqr(r);
	}
};

inline double helenSqr(double a, double b, double c) {
	double p((a + b + c) / 2.0);
	return sqrt(p * (p - a) * (p - b) * (p - c));
}

struct ypr {
	double y, s, yv;
	int v;
};
inline bool operator <(const ypr& a, const ypr& b) {
	return (a. yv < b. yv) || (a. yv == b. yv && a. v > b. v);
}

const int maxn = 1009;
const int maxx = 1002009;

int n, totx;
double x[maxx], ans;
circle a[maxn];
ypr y[maxn << 1];

double arcSqr(circle c, point a, point b) {
	vect x(a - c. o), y(b - c. o);
	double sqt(fabs(x * y));
	double d(sqrt(sqDist(a, b)));
	double tht(atan2(d / 2, sqt / d) * 2);
	//double tht(acos((x & y) / c. sr));
	sqt /= 2;
	return tht * c. sr / 2 - sqt;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	totx = 0;
	for (int i = 0; i < n; ++ i) { 
		a[i]. read();
		bool thr(0);
		for (int j = 0; j < i && !thr; ++ j)
			if (sqDist(a[i]. o, a[j]. o) <= sqr(a[i]. r - a[j]. r)) {
				if (a[i]. r > a[j]. r)
					swap(a[i], a[j]);
				thr = 1;
			}
		if (thr) {
			-- i;
			-- n;
		}
	}
	for (int i = 0; i < n; ++ i) {
		x[totx ++] = a[i]. o. x - a[i]. r;
		x[totx ++] = a[i]. o. x + a[i]. r;
	}
	for (int i = 1; i < n; ++ i)
		for (int j = 0; j < i; ++ j) {
			double sd(sqDist(a[i]. o, a[j]. o));
			if (sd <= sqr(a[i]. r + a[j]. r) && sd >= sqr(a[i]. r - a[j]. r)) {
				double d(sqrt(sd));
				double s(helenSqr(a[i]. r, a[j]. r, d));
				double h(s * 2 / d);
				double l(sqrt(a[i]. sr - sqr(h)));
				if (sqr(a[j]. r) > sd + sqr(a[i]. r))
					l = -l;
				point p(a[i]. o + (a[j]. o - a[i]. o) * (l / d));
				vect v((a[j]. o - a[i]. o). vertical());
				v = v * (h / v. len());
				x[totx ++] = p. x + v. x;
				x[totx ++] = p. x - v. x;
			}
		}
	sort(x, x + totx);
	totx = unique(x, x + totx) - x;
	ans = 0;
	for (int i = 0; i < totx - 1; ++ i) {
		double xl(x[i]), xr(x[i + 1]);
		int cy(0);
		for (int j = 0; j < n; ++ j) 
			if (a[j]. o. x - a[j]. r < xr && a[j]. o. x + a[j]. r > xl) {
				double yl(sqrt(a[j]. sr - sqr(a[j]. o. x - xl)));
				double yv(sqrt(a[j]. sr - sqr(a[j]. o. x - (xl + xr) / 2.0)));
				double yr(sqrt(a[j]. sr - sqr(a[j]. o. x - xr)));
				double ar(arcSqr(a[j], point(xl, a[j]. o. y - yl), point(xr, a[j]. o. y - yr)));
				double ym((yl + yr) / 2);
				y[cy]. y = a[j]. o. y - ym;
				y[cy]. yv = a[j]. o. y - yv;
				y[cy]. v = 1;
				y[cy]. s = ar;
				++ cy;
				y[cy]. y = a[j]. o. y + ym;
				y[cy]. yv = a[j]. o. y + yv;
				y[cy]. v = -1;
				y[cy]. s = ar;
				++ cy;
			}
		double ml(0);
		sort(y, y + cy);
		for (int j = 0, cnt = 0; j < cy - 1; ++ j) {
			if ((y[j]. v == 1 && !cnt) || (y[j]. v == -1 && cnt == 1))
				ans += y[j]. s;
			cnt += y[j]. v;
			if (cnt)
				ml += y[j + 1]. y - y[j]. y;
		}
		ans += y[cy - 1]. s;
		ans += ml * (xr - xl);
	}
	printf("%.3lf\n", ans);
}

BZOJ1223 [HNOI2002]Kathy函数

比较神奇的数学题。首先你要知道这个函数的意思是把它的二进制flip。至于怎么来的我也是听说的。然后推一下发现的确是这么回事。

 

于是就变成数位dp了。然后我发现数位dp常常可以用奇奇怪怪的方法水过去。然后又发现数组从0开始标号也会造成麻烦。+1-1啥的最讨厌了。

 

于是这题就变成高精度练习题了。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 409;

struct BigInt {
	int l, a[maxn], base;
	BigInt(int b = 10) {
		l = 0;
		base = b;
	}
	void push() {
		for (int i = 0; i < l - 1; ++ i) {
			a[i + 1] += a[i] / base;
			a[i] %= base;
		}
		for (; a[l - 1] >= base; ++ l) {
			a[l] = a[l - 1] / base;
			a[l - 1] %= base;
		}
	}
	void pull() {
		for (; l && !a[l - 1]; -- l);
	}
	void read() {
		static char g[maxn];
		base = 10;
		scanf("%s", g);
		l = strlen(g);
		for (int i = 0; i < l; ++ i)
			a[i] = g[l - i - 1] - 48;
		pull();
	}

	inline void setOne() {
		l = 1;
		a[0] = 1;
	}
	inline void setZero() {
		l = 0;
	}

	void assAdd(const BigInt& x) {
		for (int i = 0; i < l && i < x. l; ++ i)
			a[i] += x. a[i];
		for (int i = l; i < x. l; ++ i)
			a[i] = x. a[i];
		if (x. l > l)
			l = x. l;
		push();
	}
	void assDec() {
		-- a[0];
		for (int i = 0; a[i] < 0; ++ i) {
			a[i] += base;
			-- a[i + 1];
		}
		pull();
	}
	void assMul(int x) {
		for (int i = 0; i < l; ++ i)
			a[i] *= x;
		push();
	}
	int mod(int x) const {
		int s(0);
		for (int i = l - 1; i >= 0; -- i)
			s = (s * base + a[i]) % x;
		return s;
	}
	void assDiv(int x) {
		for (int i = l - 1; i >= 0; -- i) {
			if (i)
				a[i - 1] += a[i] % x * base;
			a[i] /= x;
		}
		pull();
	}
	int compareTo(const BigInt& x) const {
		if (l > x. l)
			return 1;
		else if (l < x. l)
			return -1;
		for (int i = l - 1; i >= 0; -- i)
			if (a[i] > x. a[i])
				return 1;
			else if (a[i] < x. a[i])
				return -1;
		return 0;
	}
	void ass(const BigInt& x) {
		static BigInt tmp;
		if (base == x. base) {
			l = x. l;
			memcpy(a, x. a, sizeof(a));
		}
		else {
			tmp. base = x. base;
			tmp. ass(x);
			for (l = 0; tmp. l; ++ l) {
				a[l] = tmp. mod(base);
				tmp. assDiv(base);
			}
		}
	}
	void print(char eon = 10) {
		for (int i = l - 1; i >= 0; -- i)
			putchar(a[i] + 48);
		if (!l)
			putchar(48);
		if (eon)
			putchar(eon);
	}
	void flip() {
		for (int i = 0; (i << 1) < l; ++ i)
			swap(a[i], a[l - i - 1]);
		pull();
	}
};

BigInt tmp, m, bm(2), br(2), ans, p2[maxn], ONE;

bool checkFlip(const BigInt& a) {
	int q(a. l >> 1), p(a. l - q - 1);
	for (int i = 0; p - i >= 0; ++ i)
		if (a. a[p - i] > a. a[q + i])
			return 1;
		else if (a. a[p - i] < a. a[q + i])
			return 0;
	return 1;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	ONE. setOne();
	tmp. read();
	m. ass(tmp);
	bm. ass(tmp);
	int l((bm. l + 1) >> 1);
	p2[0]. setOne();
	for (int i = 1; i <= l; ++ i) {
		p2[i]. ass(p2[i - 1]);
		p2[i]. assMul(2);
	}
	for (int i = 1; i < bm. l; ++ i)
		ans. assAdd(p2[((i + 1) >> 1) - 1]);
	for (int i = bm. l - 2; i > bm. l - l - 1; -- i)
		if (bm. a[i])
			ans. assAdd(p2[i - (bm. l >> 1)]);
	if (checkFlip(bm))
		ans. assAdd(ONE);
	ans. print();
}

BZOJ1023 [SHOI2008]cactus仙人掌图

第六百道题一定要不水!虽然离第一页还有几十道题的距离。

 

很早之前见过的题吧。然后一直觉得特别难写所以都没写。以前写点不重复的仙人掌都觉得挺痛苦,更不要说这个了。

 

现在想想还行,因为我yy出了化简的方法。先搞出dfs树。然后一个顶点如果是一个环里深度最浅的,那么称它为这个环的头。可以证明一个顶点最多在一个不以它为头的环上。把这个环记下来,其它的环都从这个顶点去找边来找就行了。这样的话可以比较节省代码量,写起来也比较明了。虽然还是比mhy长。

 

然后因为我在最后一步dp的时候写了一个static的f数组,所以光荣了。debug了好久。晕。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct edge {
	int t, av;
	edge *next, *rv;
};
struct ball {
	int l, *a, h;
};

const int maxn = 50009;
const int maxm = 200009;
const int maxarr = 400009;

int n, m, cb, fb[maxn], d[maxn];
int f[maxn], ans, fd[maxn], fe[maxn];
int stc[maxn], tst;
int ibuf_arr[maxarr], *ibuf(ibuf_arr);
bool vis[maxn];
edge elst[maxm], *ep(elst), *head[maxn];
ball b[maxm];

inline edge* addEdge(int u, int v) {
	ep-> t = v;
	ep-> av = 1;
	ep-> next = head[u];
	return head[u] = ep ++;
}

inline void upMax(int& a, int b) {
	if (a < b)
		a = b;
}

void dfsBall(int p) {
	vis[p] = 1;
	stc[++ tst] = p;
	for (edge *e = head[p]; e; e = e-> next)
		if (e-> av) {
			e-> rv-> av = 0;
			if (vis[e-> t] && (!fb[p])) {
				e-> av = 0;
				b[++ cb]. l = 0;
				b[cb]. a = ibuf;
				for (int i = tst; 1; -- i) {
					b[cb]. a[b[cb]. l ++] = stc[i];
					if (stc[i] != e-> t)
						fb[stc[i]] = cb;
					else
						break;
				}
				b[cb]. h = e-> t;
				ibuf += b[cb]. l;
			}
			else {
				d[e-> t] = d[p] + 1;
				dfsBall(e-> t);
			}
		}
	-- tst;
}

void downDP(int p) {
	fd[p] = fe[p] = 0;
	for (edge* e = head[p]; e; e = e-> next)
		if (e-> av) {
			if (fb[e-> t] && p == b[fb[e-> t]]. h) {
				int bi(fb[e-> t]), fl(0);
				for (int i = 0; i < b[bi]. l; ++ i)
					if (b[bi]. a[i] != b[bi]. h) {
						int q(b[bi]. a[i]);
						downDP(q);
						upMax(fl, fd[q] + min(d[q] - d[p], b[bi]. l - d[q] + d[p]));
					}
				if (fl > fd[p]) {
					fe[p] = fd[p];
					fd[p] = fl;
				}
				else if (fl > fe[p])
					fe[p] = fl;
			}
			else if (!fb[p] || fb[p] != fb[e-> t]) {
				downDP(e-> t);
				int v(fd[e-> t] + 1);
				if (v > fd[p]) {
					fe[p] = fd[p];
					fd[p] = v;
				}
				else if (v > fe[p])
					fe[p] = v;
			}
		}
	upMax(ans, fd[p] + fe[p]);
}

#define modi(_i_) (((_i_)>=b[bi].l)?((_i_)-b[bi].l):(_i_))
void upDP(int p, int u) {
	static int q[maxn << 1], fu[maxn << 1];
	for (edge* e = head[p]; e; e = e-> next)
		if (e-> av) {
			if (fb[e-> t] && p == b[fb[e-> t]]. h) {
				int bi(fb[e-> t]), fl(0);
				int *f(ibuf);
				ibuf += b[bi]. l;
				for (int i = 0; i < b[bi]. l; ++ i) {
					int q(b[bi]. a[i]);
					f[i] = 0;
					if (q == p)
						break;
					fu[i] = fd[q];
					upMax(fl, fd[q] + min(d[q] - d[p], b[bi]. l - d[q] + d[p]));
				}
				if (fl == fd[p])
					fu[b[bi]. l - 1] = max(u, fe[p]);
				else
					fu[b[bi]. l - 1] = max(u, fd[p]);
				int hd(0), tl(1), hl(b[bi]. l >> 1);
				q[0] = 0;
				for (int i = 1; i < (b[bi]. l << 1); ++ i) {
					int fi(modi(i));
					while (hd < tl && i - q[hd] > hl)
						++ hd;
					if (hd < tl)
						upMax(f[fi], fu[modi(q[hd])] + i - q[hd]);
					while (hd < tl && fu[modi(q[tl - 1])] - q[tl - 1] <= fu[fi] - i)
						-- tl;
					q[tl ++] = i;
				}
				hd = 0;
				tl = 1;
				q[0] = (b[bi]. l << 1) - 1;
				for (int i = (b[bi]. l << 1) - 2; i >= 0; -- i) {
					int fi(modi(i));
					while (hd < tl && q[hd] - i > hl)
						++ hd;
					if (hd < tl)
						upMax(f[fi], fu[modi(q[hd])] + q[hd] - i);
					while (hd < tl && fu[modi(q[tl - 1])] + q[tl - 1] <= fu[fi] + i)
						-- tl;
					q[tl ++] = i;
				}
				for (int i = 0; i < b[bi]. l - 1; ++ i) {
					upMax(ans, f[i] + fd[b[bi]. a[i]]);
					upDP(b[bi]. a[i], f[i]);
				}
				ibuf -= b[bi]. l;
			}
			else if (!fb[p] || fb[e-> t] != fb[p]) {
				int v((fd[e-> t] + 1 == fd[p]) ? fe[p] : fd[p]);
				upDP(e-> t, max(u, v) + 1);
			}
		}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	memset(head, 0, sizeof(head));
	for (int i = 0; i < m; ++ i) {
		int c, u, v;
		scanf("%d%d", &c, &u);
		while (-- c) {
			scanf("%d", &v);
			edge *a(addEdge(u, v)), *b(addEdge(v, u));
			a-> rv = b;
			b-> rv = a;
			u = v;
		}
	}
	memset(vis, 0, sizeof(vis));
	memset(fb, 0, sizeof(fb));
	cb = 0;
	tst = 0;
	d[1] = 1;
	dfsBall(1);
	ans = 0;
	downDP(1);
	upDP(1, 0);
	printf("%d\n", ans);
}

BZOJ2734 [HNOI2012]集合选数

最初感觉好多状态怎么做。

 

然后发现(其实是瞟了一下题解)如果把一个数分成2a*3b*c,那么c只影响上界。2a*3b中a最大是16,b最大是10,列一张17*11的表出来,然后发现右下方还有很多空东西。于是直接像插头一样状压一下轮廓线呗。反正对一个数有影响的只有它的上面和左边。最近写插头写得有点疯啊。其实它的很多思想都是可以推广出来用的。比如最小表示啥的。

 

于是如果直接这样写的话大概要跑30000多次dp,还是比较悬。然后再想想发现只有表里能取的数的形状发生改变的时候才会改变dp的答案。表里面的数很少所以改变次数也应该很少。所以记一下上一次的形状是什么然后比较相不相同,相同就直接沿用。

 

然后跑得飞快。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define _l (long long int)
#define pow2(_x_) (1<<(_x_))

const int maxn = 19;
const int maxm = 15;
const int maxv = 100000;
const int maxst = pow2(11) + 9;
const int mod = 1e9 + 1;

int f[2][maxst], vl[maxn][maxm], n, m, v;
int dis[maxn], ldis[maxn], lans, ans;

void pre() {
	for (int i = 0; i < maxn; ++ i) {
		vl[i][0] = (1 << i);
		if (vl[i][0] > maxv)
			vl[i][0] = -1;
		for (int j = 1; j < maxm; ++ j) 
			if (vl[i][j - 1] > -1) {
				vl[i][j] = vl[i][j - 1] * 3;
				if (vl[i][j] > maxv)
					vl[i][j] = -1;
			}
			else
				vl[i][j] = -1;
	}
}

bool getDis(int x) {
	memset(dis, 0, sizeof(dis));
	for (int i = 0; i < maxn; ++ i)
		while (vl[i][dis[i]] > -1 && x >= vl[i][dis[i]])
			++ dis[i];
	bool eq(1);
	for (int i = 0; i < maxn && eq; ++ i)
		if (dis[i] != ldis[i])
			eq = 0;
	if (!eq)
		memcpy(ldis, dis, sizeof(dis));
	return eq;
}

inline void incm(int& _a_, int _b_) {
	_a_ += _b_;
	if (_a_ >= mod)
		_a_ -= mod;
}

int dp() {
	int prv(1), cur(0), m(dis[0]), e(pow2(m));
	memset(f, 0, sizeof(f));
	f[cur][0] = 1;
	for (int i = 0; dis[i]; ++ i)
		for (int j = 0; j < dis[i]; ++ j) {
			swap(cur, prv);
			memset(f[cur], 0, sizeof(f[cur]));
			for (int k = 0; k < e; ++ k)
				if (f[prv][k]) {
					if (!(k & pow2(j)) && (!j || !(k & pow2(j - 1))))
						incm(f[cur][k | pow2(j)], f[prv][k]);
					incm(f[cur][k & (~pow2(j))], f[prv][k]);
				}
		}
	int ans(0);
	for (int i = 0; i < e; ++ i)
		incm(ans, f[cur][i]);
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	pre();
	scanf("%d", &v);
	ans = 1;
	lans = 0;
	memset(ldis, -1, sizeof(ldis));
	ans = 1;
	for (int i = 1; i <= v; ++ i)
		if (i % 2 && i % 3) {
			if (getDis(v / i))
				ans = _l ans * lans % mod;
			else
				ans = _l ans * (lans = dp()) % mod;
		}
	printf("%d\n", ans);
}

BZOJ3242 [Noi2013]快餐店

好吧我的本意是想学习一下dp的。然后发现了这道纠结了一年半还多的题。

 

首先把环找出来。然后环上会长一些树。先把树的答案求了,因为它很好求。

 

然后考虑环,那么答案应该是随便断一条边之后答案的最小值(而不是最大值)。于是乎记录一下每个点按某个方向到起点的距离,记为d[i]。算一下每个点的树的深度,记为l[i]。于是每次要最大化l[i]+l[j]+|d[i]-d[j]|。反正是最大化所以顺序是没有关系的,但是不能自交。这是个比较容易错的地方。于是就去写个线段树来维护吧。只查询不修改让我总有能线性搞的想法,虽然没想清楚具体怎么实现。

 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>

using namespace std;

struct edge {
	int t, v;
	edge *next;
};

typedef long long dint;
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define _l (long long int)

const int maxn = 100009;

edge elst[maxn << 1], *ep(elst), *head[maxn];
int n, fa[maxn], df[maxn], ol[maxn], d[maxn];
int tlp, lp[maxn << 1];
dint lv[maxn << 1], le[maxn << 1], md[maxn], ans;

inline void addEdge(int u, int v, int w) {
	ep-> t = v;
	ep-> v = w;
	ep-> next = head[u];
	head[u] = ep ++;
}

void findLoop() {
	static int q[maxn];
	int hd(0), tl(1);
	int uo(0), vo(0), dx, a;
	q[0] = 1;
	fa[1] = -1;
	d[1] = 1;
	memset(ol, 0, sizeof(ol));
	memset(fa, 0, sizeof(fa));
	while (hd < tl && !uo) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (e-> t ^ fa[p]) {
				if (fa[e-> t]) {
					uo = p;
					vo = e-> t;
					dx = e-> v;
					break;
				}
				else {
					d[e-> t] = d[p] + 1;
					fa[e-> t] = p;
					df[e-> t] = e-> v;
					q[tl ++] = e-> t;
				}
			}
	}
	tlp = 0;
	for (int u = uo, v = vo; u ^ v; ++ tlp)
		if (d[u] > d[v])
			a = u = fa[u];
		else
			a = v = fa[v];
	++ tlp;
	for (int u = uo, v = vo; u ^ v; )
		if (d[u] > d[v]) {
			int i(d[uo] - d[u]);
			lp[i] = u;
			le[i] = df[u];
			u = fa[u];
		}
		else {
			int i(tlp - d[vo] + d[v] - 1);
			lp[i] = v;
			le[i - 1] = df[v];
			v = fa[v];
		}
	lp[d[uo] - d[a]] = a;
	le[tlp - 1] = dx;
	for (int i = 0; i < tlp; ++ i)
		ol[lp[i]] = 1;
}

void dpTree(int p0) {
	static int q[maxn];
	int hd(0), tl(1);
	q[0] = p0;
	while (hd < tl) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (!ol[e-> t]) {
				ol[e-> t] = ol[p] + 1;
				q[tl ++] = e-> t;
			}
	}
	for (int i = tl - 1; i >= 0; -- i) {
		int p(q[i]);
		dint s2(0);
		md[p] = 0;
		for (edge* e = head[p]; e; e = e-> next)
			if (ol[e-> t] == ol[p] + 1) {
				dint v(md[e-> t] + e-> v);
				if (v > md[p]) {
					s2 = md[p];
					md[p] = v;
				}
				else if (v > s2)
					s2 = v;
			}
		if (md[p] + s2 > ans)
			ans = md[p] + s2;
	}
}

struct dat {
	dint x, y, s;
	dat() {}
	dat(dint x0, dint y0) {
		x = x0, y = y0, s = 0;
	}
	dat(dat a, dat b) {
		s = max(max(a. s, b. s), a. x + b. y);
		x = max(a. x, b. x);
		y = max(a. y, b. y);
	}
};
struct seg {
	int l, r;
	dat d;
	seg *ls, *rs;
};
seg slst[maxn * 5], *sp(slst), *rt;

#define midp(p) ((p->l+p->r)>>1)
inline seg* sgtBuild(int l, int r) {
	seg *p(sp ++);
	p-> l = l;
	p-> r = r;
	if (l + 1 == r) {
		p-> d = dat(lv[l] + le[l], lv[l] - le[l]);
	}
	else {
		p-> ls = sgtBuild(l, midp(p));
		p-> rs = sgtBuild(midp(p), r);
		p-> d = dat(p-> ls-> d, p-> rs-> d);
	}
	return p;
}
inline dat sgtQry(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r)
		return p-> d;
	else if (r <= midp(p))
		return sgtQry(p-> ls, l, r);
	else if (l >= midp(p))
		return sgtQry(p-> rs, l, r);
	else
		return dat(sgtQry(p-> ls, l, midp(p)), sgtQry(p-> rs, midp(p), r));
}

void dpLoop() {
	dint totl(0);
	for (int i = 0; i < tlp; ++ i) {
		lv[i + tlp] = lv[i];
		le[i + tlp] = le[i];
		lp[i + tlp] = lp[i];
		totl += le[i];
	}
	le[tlp * 2] = 0;
	for (int i = tlp * 2 - 1; i >= 0; -- i)
		le[i] += le[i + 1];
	rt = sgtBuild(0, tlp * 2);
	dint cans(0x7fffffffffffffffll);
	for (int i = 0; i < tlp; ++ i) {
		cans = min(cans, sgtQry(rt, i, i + tlp). s);
	}
	ans = max(ans, cans);
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	memset(head, 0, sizeof(head));
	for (int i = 0; i < n; ++ i) {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		addEdge(u, v, w);
		addEdge(v, u, w);
	}
	findLoop();
	ans = 0;
	for (int i = 0; i < tlp; ++ i) {
		dpTree(lp[i]);
		lv[i] = md[lp[i]];
	}
	dpLoop();
	printf("%.1lf\n", (double)ans / 2.0);
}

BZOJ1079 [SCOI2008]着色方案

看上去比较不可做的DP。好像别人写的记搜跑得飞快。

 

我看我是写最小表示写上瘾了。

 

压一下状态,有点像今天第二题。算每种值的有多少。然后直接跑。

 

感觉废状态比较多啊,跑得好慢。另外用memset清空高维数组会比较快。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int mod = 1e9 + 7;
const int maxn = 17;
const int maxst = 16009;

#define mbit(_a_,_b_) ((_a_)<<((_b_)<<2))
#define gbit(_a_,_b_) (((_a_)>>((_b_)<<2))&0xf)
#define _l (long long int)

int m, c[maxn], n, mt, tots, slst[maxst];
int f[2][7][maxst];

void dfsState(int t, int l, int v) {
	if (l == mt + 1) {
		slst[tots ++] = v | t;
	}
	else {
		for (int i = 0; i <= t; ++ i)
			dfsState(t - i, l + 1, v | mbit(i, l));
	}
}

inline int fState(int x) {
	return lower_bound(slst, slst + tots, x) - slst;
}

inline void incm(int& a, int b) {
	a += b;
	if (a >= mod)
		a -= mod;
}

#define clearFCur() { \
	for (int i = 1; i <= mt; ++ i) \
		for (int j = 0; j < tots; ++ j) \
			f[cur][i][j] = 0; \
}

int dp() {
	int cur(0), prv(1);
	int sb(0);
	for (int i = 0; i <= mt; ++ i)
		sb |= mbit(c[i], i);
	memset(f, 0, sizeof(f));
	for (int i = 1; i <= mt; ++ i)
		if (gbit(sb, i) > 0)
			f[cur][i - 1][fState(sb - mbit(1, i) + mbit(1, i - 1))] = gbit(sb, i);
	for (int ti = 1; ti < n; ++ ti) {
		swap(cur, prv);
//		clearFCur();
		memset(f[cur], 0, sizeof(f[cur]));
		for (int i = 0; i <= mt; ++ i)
			for (int j = 0; j < tots; ++ j)
				if (f[prv][i][j]) {
					int s(slst[j]), c;
					for (int k = 1; k <= mt; ++ k)
						if ((c = gbit(s, k) - (k == i)))
							incm(f[cur][k - 1][fState(s - mbit(1, k) + mbit(1, k - 1))], _l f[prv][i][j] * c % mod);
				}
	}
	return f[cur][0][fState(m)];
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &m);
	memset(c, 0, sizeof(c));
	mt = 0;
	n = 0;
	for (int i = 0; i < m; ++ i) {
		int tmp;
		scanf("%d", &tmp);
		n += tmp;
		++ c[tmp];
		if (tmp > mt)
			mt = tmp;
	}
	tots = 0;
	dfsState(m, 1, 0);
	sort(slst, slst + tots);
	printf("%d\n", dp());
}

BZOJ2770 YY的Treap

今天考试题的弱化版。只用求LCA是谁,不用求它的深度。所以直接用线段树找下区间最值就搞定了。然后long的范围有负数坑了一发。

 

好像在不平衡的平衡树上的题也比较热啊。而且我还做得比较少。是要补一补。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef pair <int, int> vpr;
typedef pair <bool, vpr> dpr;
struct seg {
	int l, r;
	dpr d;
	seg *ls, *rs;
};
struct query {
	int t, a, b;
};

const int maxn = 400009;

int n, m, vl[maxn], tv;
vpr a[maxn];
query q[maxn];
seg slst[maxn * 3], *sp(slst), *rt;

inline int gpos(int x) {
	return lower_bound(vl, vl + tv, x) - vl;
}

#define midp(p) ((p->l+p->r)>>1)
inline seg* sgtBuild(int l, int r) {
	seg* p(sp ++);
	p-> l = l;
	p-> r = r;
	p-> d = dpr(1, vpr(0, 0));
	if (l + 1 < r) {
		p-> ls = sgtBuild(l, midp(p));
		p-> rs = sgtBuild(midp(p), r);
	}
	return p;
}
inline void sgtChg(seg* p, int p0, dpr v0) {
	if (p-> l + 1 == p-> r)
		p-> d = v0;
	else {
		if (p0 < midp(p))
			sgtChg(p-> ls, p0, v0);
		else 
			sgtChg(p-> rs, p0, v0);
		p-> d = min(p-> ls-> d, p-> rs-> d);
	}
}
inline dpr sgtQry(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r)
		return p-> d;
	else if (r <= midp(p))
		return sgtQry(p-> ls, l, r);
	else if (l >= midp(p))
		return sgtQry(p-> rs, l, r);
	else
		return min(sgtQry(p-> ls, l, midp(p)), sgtQry(p-> rs, midp(p), r));
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	tv = 0;
	for (int i = 0; i < n; ++ i) {
		scanf("%d", &a[i]. second);
		vl[tv ++] = a[i]. second;
	}
	for (int i = 0; i < n; ++ i)
		scanf("%d", &a[i]. first);
	for (int i = 0; i < m; ++ i) {
		char opt[3];
		scanf("%s", opt);
		if (opt[0] == 'I') {
			q[i]. t = 1;
			scanf("%d%d", &q[i]. a, &q[i]. b);
			vl[tv ++] = q[i]. a;
		}
		else if (opt[0] == 'D') {
			q[i]. t = 2;
			scanf("%d", &q[i]. a);
		}
		else {
			q[i]. t = 3;
			scanf("%d%d", &q[i]. a, &q[i]. b);
		}
	}
	sort(vl, vl + tv);
	tv = unique(vl, vl + tv) - vl;
	rt = sgtBuild(0, tv);
	for (int i = 0; i < n; ++ i)
		sgtChg(rt, gpos(a[i]. second), dpr(0, a[i]));
	for (int i = 0; i < m; ++ i)
		if (q[i]. t == 1)
			sgtChg(rt, gpos(q[i]. a), dpr(0, vpr(q[i]. b, q[i]. a)));
		else if (q[i]. t == 2)
			sgtChg(rt, gpos(q[i]. a), dpr(1, vpr(0, 0)));
		else {
			int u(gpos(q[i]. a)), v(gpos(q[i]. b));
			if (u > v)
				swap(u, v);
			printf("%d\n", sgtQry(rt, u, v + 1). second. second);
		}
}

BZOJ1187 [HNOI2007]神奇游乐园

我的第二道插头DP。差不多纠结了一天。

 

这道题网上的题解都讲的好简略啊。最后还得自己yy。没有用括号序列法让我觉得自己比较厉害。

 

考虑轮廓线上的m个格子,有3种情况:没有插头,有且只能往一边走,有且要往两边走。对于第二种,要用最多3个不同的值来记录连通性。还要有两个不同的值来表示第一种情况和第三种情况。我比较懒,所以直接用一个八进制数去表示,然后每次二分找编号。

 

考虑转移,分一堆情况进行讨论。默认从左上朝右下走。

 

首先是要走当前格子的情况。

 

如果左边和上面都有插头,那么看它们是否连通。如果不连通则把它们连通,如果已经连通,看是否还有其它插头。没有就更新答案,否则忽略。

 

如果只有左边有插头,再分类。如果它是第二种,那么这个格子可以新建一个第三种插头,或者把左边格子的插头接过来。如果它是第三种那么它必需把左边格子接过来。

 

如果只有上面有插头,那么必需把上面的插头接上来。

 

如果左边和上面都没有插头,那么可以新建一个第三种插头。

 

然后如果不走这个格子的话,要求上面没有插头,且左边不是第三种插头。

 

按mhy的话说,插头dp就是写起来很麻烦。的确。不过写出来也还是挺高兴的。

 

然后代码丑到不能看了。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 109;
const int maxm = 9;
const int maxst = 50009;
const int inf = 0x3f3f3f3f;

#define pow8(_x_) (1<<(_x_)<<(_x_)<<(_x_))
#define mbit(_x_,_y_) ((_x_)<<(_y_)<<(_y_)<<(_y_))
#define gbit(_x_,_y_) (((_x_)>>(_y_)>>(_y_)>>(_y_))&7)

int f[2][maxst], n, m, a[maxn][maxm], ans;
int slst[maxst], tots;

inline void upMax(int& a, int b) {
	if (a < b)
		a = b;
}

void dfsState(int cur, int tot, int val, int e) {
	if (cur == m) {
		static int cnt[maxm];
		memset(cnt, 0, sizeof(cnt));
		for (int i = 0; i < m; ++ i)
			++ cnt[gbit(val, i)];
		bool lg(1);
		for (int i = 1; i <= tot && lg; ++ i)
			if (cnt[i] != 0 && cnt[i] != 2)
				lg = 0;
		if (lg)
			slst[tots ++] = val;
	}
	else {
		for (int i = 0; i <= tot; ++ i)
			dfsState(cur + 1, tot, val | mbit(i, cur), e);
		dfsState(cur + 1, tot + 1, val | mbit(tot + 1, cur), e);
		if (e)
			dfsState(cur + 1, tot, val | mbit(7, cur), 0);
	}
}
void preState() {
	tots = 0;
	dfsState(0, 0, 0, 1);
	sort(slst, slst + tots);
}

int fState(int s) {
	int ret(lower_bound(slst, slst + tots, s) - slst);
	if (slst[ret] != s)
		puts("naive");
	return ret;
}

int joinState(int s, int p) {
	static int z[maxm], y[maxn], c;
	for (int i = 0; i < m; ++ i)
		z[i] = gbit(s, i);
	if (z[p - 1] == 7) {
		z[p - 1] = z[p];
		z[p] = 0;
	}
	else {
		int f(z[p]), t(z[p - 1]);
		for (int i = 0; i < m; ++ i)
			if (z[i] == f)
				z[i] = t;
		z[p - 1] = 0;
		z[p] = 0;
		memset(y, 0, sizeof(y));
		c = 0;
		for (int i = 0; i < m; ++ i)
			if (z[i] && z[i] < 7) {
				if (y[z[i]]) {
					z[i] = y[z[i]];
				}
				else {
					y[z[i]] = ++ c;
					z[i] = y[z[i]];
				}
			}
	}
	int f(0);
	for (int i = 0; i < m; ++ i)
		f |= mbit(z[i], i);
	return fState(f);
}

int expState(int s, int p) {
	static int z[maxm];
	for (int i = 0; i < m; ++ i)
		z[i] = gbit(s, i);
	int f(1);
	for (int i = 0; i < p - 1; ++ i)
		if (z[i] < 7 && z[i] + 1 > f)
			f = z[i] + 1;
	for (int i = 0; i < m; ++ i)
		if (z[i] < 7 && z[i] >= f)
			++ z[i];
	z[p - 1] = f;
	z[p] = f;
	f = 0;
	for (int i = 0; i < m; ++ i)
		f |= mbit(z[i], i);
	return fState(f);
}
int extState(int s, int p) {
	static int z[maxm];
	for (int i = 0; i < m; ++ i)
		z[i] = gbit(s, i);
	z[p] = z[p - 1];
	z[p - 1] = 0;
	int f(0);
	for (int i = 0; i < m; ++ i)
		f |= mbit(z[i], i);
	return fState(f);
}

int forkState(int s, int p) {
	return fState(s | mbit(7, p));
}

bool legalState(int s, int p) {
	static int z[maxm];
	for (int i = 0; i < m; ++ i)
		z[i] = gbit(s, i);
	for (int i = 0; i < p - 1; ++ i)
		if (z[i])
			return 0;
	for (int i = p + 1; i < m; ++ i)
		if (z[i])
			return 0;
	return 1;
}

void fillArr(int* a, int sz, int v) {
	for (int i = 0; i < sz; ++ i)
		a[i] = v;
}

void dp() {
	int cur(0), prv(1);
	fillArr(f[cur], tots, -inf);
	f[cur][0] = 0;
	for (int x = 0; x < n; ++ x) {
		for (int y = 0; y < m; ++ y) {
			swap(cur, prv);
			fillArr(f[cur], tots, -inf);
			f[cur][0] = 0;
			for (int i = 0; i < tots; ++ i) 
				if (f[prv][i] > -inf) {
					int s(slst[i]);
					if (gbit(s, y) && y && gbit(s, y - 1)) {
						if (gbit(s, y) == gbit(s, y - 1)) {
							if (legalState(s, y))
								upMax(ans, f[prv][i] + a[x][y]);
						}
						else {
							upMax(f[cur][joinState(s, y)], f[prv][i] + a[x][y]);
						}
					}
					if (!gbit(s, y) && y && gbit(s, y - 1)) {
						if (gbit(s, y - 1) == 7)
							upMax(f[cur][expState(s, y)], f[prv][i] + a[x][y]);
						else
							upMax(f[cur][extState(s, y)], f[prv][i] + a[x][y]);
					}
					if (gbit(s, y) && (!y || gbit(s, y - 1) != 7))
						upMax(f[cur][i], f[prv][i] + a[x][y]);
					if (y < m - 1 && !gbit(s, y) && (!y || gbit(s, y - 1) != 7))
						upMax(f[cur][forkState(s, y)], f[prv][i] + a[x][y]);
					if (!gbit(s, y) && (!y || gbit(s, y - 1) != 7))
						upMax(f[cur][i], f[prv][i]);
				}
		}
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	preState();
	ans = -inf;
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < m; ++ j)
			scanf("%d", a[i] + j);
	ans = -inf;
	dp();
	printf("%d\n", ans);
}

BZOJ3438 小M的作物

我以为是最大权闭合子图来着。然后好像就直接最小割就行了吧。突然觉得我的网络流好弱。

 

建源汇,分别连每个作物。对于每个组合建两个点分别边源汇,然后中间像源汇一样连它的成员。如果全在一边,那么会有一条边不用割。这也是一个集合都选对答案有贡献的建图方式。

 

然后就完了。建图和dinic都写得有些丑,所以调了半天。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct edge {
	int t, c;
	edge *next, *rv;
};

const int maxn = 3009;
const int inf = 0x3f3f3f3f;

int n, m, c, d[maxn], t, st, te;
edge *head[maxn];

inline edge* newEdge(int u, int v, int c) {
	edge* e(new edge);
	e-> t = v;
	e-> c = c;
	e-> next = head[u];
	return head[u] = e;
}
inline void addEdge(int u, int v, int c) {
	edge *a(newEdge(u, v, c)), *b(newEdge(v, u, 0));
	a-> rv = b;
	b-> rv = a;
}

bool bfsBuild() {
	static int q[maxn];
	int hd(0), tl(1);
	memset(d, 0, sizeof(d));
	d[q[0] = st] = 1;
	while (hd < tl && !d[te]) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (e-> c && !d[e-> t]) {
				d[e-> t] = d[p] + 1;
				q[tl ++] = e-> t;
			}
	}
	return d[te];
}
int dfsFind(int p, int c) {
	if (p == te)
		return c;
	int s(0);
	edge *e;
	for (e = head[p]; e && c; e = e-> next)
		if (e-> c && d[e-> t] == d[p] + 1) {
			int x(dfsFind(e-> t, min(c, e-> c)));
			s += x;
			c -= x;
			e-> c -= x;
			e-> rv-> c += x;
		}
	if (!e)
		d[p] = 0;
	return s;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	c = 0;
	scanf("%d", &n);
	st = n;
	te = n + 1;
	c = n + 2;
	t = 0;
	memset(head, 0, sizeof(head));
	for (int i = 0; i < n; ++ i) {
		int a;
		scanf("%d", &a);
		t += a;
		addEdge(st, i, a);
	}
	for (int i = 0; i < n; ++ i) {
		int a;
		scanf("%d", &a);
		t += a;
		addEdge(i, te, a);
	}
	scanf("%d", &m);
	for (int i = 0; i < m; ++ i) {
		int k, c1, c2, u, v;
		scanf("%d%d%d", &k, &c1, &c2);
		t += c1 + c2;
		u = c ++;
		v = c ++;
		addEdge(st, u, c1);
		addEdge(v, te, c2);
		for (int i = 0; i < k; ++ i) {
			int x;
			scanf("%d", &x);
			-- x;
			addEdge(u, x, inf);
			addEdge(x, v, inf);
		}
	}
	while (bfsBuild())
		t -= dfsFind(st, inf);
	printf("%d\n", t);
}

BZOJ3888 [Usaco2015 Jan]Stampede

usaco的银组都开始考这种题了么。其实还是挺水的,不过题号比较好。

 

就按y排序,挨个把时间轴上的东西扔掉就完了。写离散可能还没有直接写smt快呢。

 

然后线段有一边要减1有点坑。

 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long dint;
#define _l (long long int)

struct cow {
	int y;
	int ta, tb;
};

struct node {
	int l, r, w;
	node *ls, *rs;
};
typedef pair <node*, node* > npr;

const int maxn = 50009;

inline bool operator <(const cow& a, const cow& b) {
	return a. y < b. y;
}

int n, ans;
cow a[maxn];
node nlst[maxn << 1], *np(nlst), *rt;

node* newNode(int l, int r) {
	np-> l = l;
	np-> r = r;
#ifdef WIN32
	np-> w = (rand() << 16) | rand();
#else
	np-> w = rand();
#endif
	np-> ls = np-> rs = 0;
	return np ++;
}

npr split(node* p, int x) {
	if (!p)
		return npr(0, 0);
	else if (x < p-> l) {
		npr g(split(p-> ls, x));
		p-> ls = g. second;
		return npr(g. first, p);
	}
	else if (x >= p-> r) {
		npr g(split(p-> rs, x));
		p-> rs = g. first;
		return npr(p, g. second);
	}
	else {
		node *q(newNode(p-> l, x));
		q-> ls = p-> ls;
		p-> ls = 0;
		p-> l = x + 1;
		return npr(q, p);
	}
}
node *merge(node* p, node* q) {
	if (!p)
		return q;
	else if (!q)
		return p;
	else if (p-> w > q-> w) {
		p-> rs = merge(p-> rs, q);
		return p;
	}
	else {
		q-> ls = merge(p, q-> ls);
		return q;
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	srand(239473);
	scanf("%d", &n);
	for (int i = 0; i < n; ++ i) {
		int x, r;
		scanf("%d%d%d", &x, &a[i]. y, &r);
		a[i]. ta = - x * r - r + 1;
		a[i]. tb = - x * r;
	}
	rt = newNode(-1, 1000000009);
	ans = 0;
	sort(a, a + n);
	for (int i = 0; i < n; ++ i) {
		npr x(split(rt, a[i]. ta - 1));
		npr y(split(x. second, a[i]. tb));
		if (y. first)
			++ ans;
		rt = merge(x. first, y. second);
	}
	printf("%d\n", ans);
}

BZOJ1455 罗马游戏

左偏树+并查集的一眼题嘛。然后合并的时候搞忘了如果两个人已经在同一个团里要跳过,于是左偏树自交去了。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define szof(p) ((p)?(p->sz):0)
struct node {
	int sz, vl, id;
	node *ls, *rs;
	node() {}
	node(int v, int i) {
		vl = v;
		id = i;
		sz = 1;
		ls = rs = 0;
	}
	inline void update() {
		if (szof(ls) < szof(rs))
			swap(ls, rs);
	}
};
node *merge(node* p, node* q) {
	if (!p)
		return q;
	else if (!q)
		return p;
	else if (p-> vl < q-> vl) {
		p-> rs = merge(p-> rs, q);
		p-> update();
		return p;
	}
	else {
		q-> rs = merge(q-> rs, p);
		q-> update();
		return q;
	}
}
node *pop(node* p) {
	if (p)
		return merge(p-> ls, p-> rs);
	else
		return 0;
}

const int maxn = 1000009;

int n, m, r[maxn];
bool al[maxn];
node nlst[maxn], *rt[maxn];

int getRoot(int x) {
	register int p, q;
	for (p = r[x]; p ^ r[p]; p = r[p]);
	for (; x ^ p; q = r[x], r[x] = p, x = q);
	return x;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i) {
		int w;
		scanf("%d", &w);
		nlst[i] = node(w, i);
		rt[i] = &nlst[i];
		al[i] = 1;
		r[i] = i;
	}
	scanf("%d", &m);
	while (m --) {
		char opt[3];
		int a, b;
		scanf("%s", opt);
		if (opt[0] == 'M') {
			scanf("%d%d", &a, &b);
			if (al[a] && al[b]) {
				a = getRoot(a);
				b = getRoot(b);
				if (a == b)
					continue;
				r[a] = b;
				rt[b] = merge(rt[b], rt[a]);
			}
		}
		else if (opt[0] == 'K') {
			scanf("%d", &a);
			if (!al[a])
				puts("0");
			else {
				a = getRoot(a);
				printf("%d\n", rt[a]-> vl);
				al[rt[a]-> id] = 0;
				rt[a] = pop(rt[a]);
			}
		}
	}
}

BZOJ1295 [SCOI2009]最长距离

多老的省选题了。

 

就处理一下任意两个格子之间最少要经过几个1,如果小于等于t就更新答案。

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

#define mkword(_x_,_y_) (((_x_)<<16)|(_y_))
#define hiword(_x_) ((_x_)>>16)
#define loword(_x_) ((_x_)&0x0000ffff)

const int maxn = 33;
const int maxnd = 909;
const int movx[4] = {0, 0, 1, -1};
const int movy[4] = {1, -1, 0, 0};

typedef pair <int, int> dpr;

inline int sqr(int x) {
	return x * x;
}

int n, m, t, d[maxn][maxn], ans;
int mp[maxn][maxn];
dpr hp[maxnd << 2];

void dijkstra(int i0, int j0) {
	int th(1);
	hp[0] = dpr(-mp[i0][j0], mkword(i0, j0));
	memset(d, -1, sizeof(d));
	while (th) {
		dpr g(hp[0]);
		pop_heap(hp, hp + th --);
		int x(hiword(g. second)), y(loword(g. second));
		if (d[x][y] > -1)
			continue;
		d[x][y] = - g. first;
		for (int mi = 0; mi < 4; ++ mi) {
			int i(x + movx[mi]), j(y + movy[mi]);
			if (i > 0 && i <= n && j > 0 && j <= m && d[i][j] == -1) {
				hp[th] = dpr(g. first - mp[i][j], mkword(i, j));
				push_heap(hp, hp + ++ th);
			}
		}
	}
	for (int i = 1; i <= n; ++ i)
		for (int j = 1; j <= m; ++ j)
			if (d[i][j] <= t) {
				int ds(sqr(i - i0) + sqr(j - j0));
				if (ds > ans)
					ans = ds;
			}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d%d", &n, &m, &t);
	for (int i = 1; i <= n; ++ i) {
		char g[maxn];
		scanf("%s", g + 1);
		for (int j = 1; j <= m; ++ j)
			mp[i][j] = g[j] - 48;
	}
	ans = 0;
	for (int i = 1; i <= n; ++ i)
		for (int j = 1; j <= m; ++ j)
			dijkstra(i, j);
	printf("%.6lf\n", sqrt(ans));
}

BZOJ3757 苹果树

今天晚上的效率比白天高啊。

 

树上莫队的另一道题。比糖果公园好写到哪里去了。

 

然后手写链表丑掉了导致调试半天+RE了一发。我还是太年轻了。

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

typedef struct node {
	int t;
	node *next;
} edge;
const int maxn = 50009;
const int maxm = 100009;
const int maxl = 17;
node nlst[maxn], *np(nlst);
struct chain {
	int sz;
	node *hd, *tl;
	chain() {
		hd = tl = 0;
		sz = 0;
	}
	inline void clear() {
		hd = tl = 0;
		sz = 0;
	}
	inline void push(int x) {
		np-> t = x;
		np-> next = 0;
		if (sz) {
			tl-> next = np;
			tl = np;
		}
		else {
			hd = np;
			tl = np;
		}
		++ np;
		++ sz;
	}
	inline void merge(chain a) {
		if (a. sz) {
			if (sz) {
				tl-> next = a. hd;
				tl = a. tl;
			}
			else {
				hd = a. hd;
				tl = a. tl;
			}
			sz += a. sz;
		}
	}
};
struct query {
	int a, b, x, y, i;
};

int n, m, bsz, ans[maxm], cnt[maxn], fb[maxn], col[maxn], tb;
int d[maxn], ath[maxn][maxl];
edge elst[maxn << 1], *ep(elst), *head[maxn];
query q[maxm];

inline bool operator <(const query& a, const query& b) {
	return (fb[a. a] < fb[b. a]) || (fb[a. a] == fb[b. a] && fb[a. b] < fb[b. b]);
}

inline void addEdge(int u, int v) {
	ep-> t = v;
	ep-> next = head[u];
	head[u] = ep ++;
}

chain dfs(int p) {
	for (int i = 1; i < maxl; ++ i)
		ath[p][i] = ath[ath[p][i - 1]][i - 1];
	chain w;
	for (edge* e = head[p]; e; e = e-> next)
		if (e-> t != ath[p][0]) {
			ath[e-> t][0] = p;
			d[e-> t] = d[p] + 1;
			chain g(dfs(e-> t));
			w. merge(g);
			if (w. sz >= bsz) {
				++ tb;
				for (node* it = w. hd; it; it = it-> next)
					fb[it-> t] = tb;
				w. clear();
			}
		}
	w. push(p);
	return w;
}
void divide() {
	d[0] = 1;
	tb = 0;
	chain g(dfs(0));
	for (node* it = g. hd; it; it = it-> next)
		fb[it-> t] = tb;
}

int LCA(int u, int v) {
	if (d[u] < d[v])
		swap(u, v);
	for (int i = maxl - 1; i >= 0; -- i)
		if (d[ath[u][i]] >= d[v])
			u = ath[u][i];
	if (u == v)
		return u;
	for (int i = maxl - 1; i >= 0; -- i)
		if (ath[u][i] ^ ath[v][i]) {
			u = ath[u][i];
			v = ath[v][i];
		}
	return ath[u][0];
}
inline void rmVertex(int p, int& ans) {
	if (p && ! -- cnt[col[p]])
		-- ans;
}
inline void insVertex(int p, int& ans) {
	if (p && !(cnt[col[p]] ++))
		++ ans;
}
void moveVertex(int& u, int v, int g, int& ans) {
	int a(LCA(u, v)), t(LCA(a, g));
	if (a == t) {
		int x(LCA(u, g)), y(LCA(v, g));
		if (d[x] > d[y])
			t = x;
		else
			t = y;
		if (LCA(t, u) == t) {
			for (int p = g; p ^ t; p = ath[p][0])
				insVertex(p, ans);
			for (int p = u; p ^ t; p = ath[p][0])
				rmVertex(p, ans);
		}
		else {
			for (int p = g; p ^ t; p = ath[p][0])
				insVertex(p, ans);
			for (int p = u; p ^ a; p = ath[p][0])
				rmVertex(p, ans);
			for (int p = ath[t][0]; p ^ a; p = ath[p][0])
				rmVertex(p, ans);
			rmVertex(a, ans);
		}
	}
	else {
		for (int p = u; p ^ a; p = ath[p][0])
			rmVertex(p, ans);
		for (int p = g; p ^ t; p = ath[p][0])
			insVertex(p, ans);
		insVertex(t, ans);
		for (int p = ath[a][0]; p ^ t; p = ath[p][0])
			insVertex(p, ans);
	}
	u = g;
}
void cptMo() {
	int u(1), v(1), cans(1);
	memset(cnt, 0, sizeof(cnt));
	cnt[col[1]] = 1;
	sort(q, q + m);
	for (int i = 0; i < m; ++ i) {
		moveVertex(u, v, q[i]. a, cans);
		moveVertex(v, u, q[i]. b, cans);
		ans[q[i]. i] = cans;
		if ((q[i]. x || q[i]. y) && q[i]. x != q[i]. y)
			ans[q[i]. i] -= (cnt[q[i]. x] && cnt[q[i]. y]);
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	bsz = (int)pow(n, 0.5) + 1;
	for (int i = 1; i <= n; ++ i)
		scanf("%d", col + i);
	memset(head, 0, sizeof(head));
	for (int i = 0; i < n; ++ i) {
		int u, v;
		scanf("%d%d", &u, &v);
		addEdge(u, v);
		addEdge(v, u);
	}
	for (int i = 0; i < m; ++ i) {
		scanf("%d%d%d%d", &q[i]. a, &q[i]. b, &q[i]. x, &q[i]. y);
		q[i]. i = i;
	}
	divide();
	for (int i = 0; i < m; ++ i)
		if (fb[q[i]. a] > fb[q[i]. b])
			swap(q[i]. a, q[i]. b);
	cptMo();
	for (int i = 0; i < m; ++ i)
		printf("%d\n", ans[i]);
}

BZOJ1415 [Noi2005]聪聪和可可

最近很颓啊。

 

这题很久之前看过。当时以为是高消就没管。然后开坑之神idy做了之后仔细一想才知道不是。

 

这题的做法是dp。首先因为边数少所以不用floyd,直接bfs预处理出所有t[i][j]表狼在i,兔子在j的时候狼下一步去哪。然后有一个结论是每一步两个玩意的距离至少缩短1,所以状态是不会有环的,这也是为啥可以不用高消。然后坑点是不能直接开状态队列,因为那不是拓补序。要先把每个点的入度算出来再跑dp。结局是我的代码超长。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct edge {
	int t;
	edge *next;
};

#define mkword(_x_,_y_) (((_x_)<<16)|(_y_))
#define hiword(_x_) ((_x_)>>16)
#define loword(_x_) ((_x_)&0x0000ffff)

const int maxn = 1009;
const int maxst = 1000009;
const int inf = 0x3f3f3f3f;

int n, m, st, te, deg[maxn], d[maxn], t[maxn][maxn], ind[maxn][maxn];
edge elst[maxn << 1], *ep(elst), *head[maxn];
double f[maxn][maxn], g[maxn][maxn], c[maxn], ans;

inline void addEdge(int u, int v) {
	ep-> t = v;
	ep-> next = head[u];
	head[u] = ep ++;
}

void bfs(int p0) {
	static int q[maxn];
	int hd(0), tl(1);
	memset(d, -1, sizeof(d));
	d[q[0] = p0] = 0;
	while (hd < tl) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (d[e-> t] == -1) {
				d[e-> t] = d[p] + 1;
				q[tl ++] = e-> t;
			}
	}
	for (int p = 1; p <= n; ++ p)
		if (p == p0) {
			t[p][p] = p;
		}
		else {
			t[p][p0] = inf;
			for (edge* e = head[p]; e; e = e-> next)
				if (d[p] == d[e-> t] + 1 && e-> t < t[p][p0])
					t[p][p0] = e-> t;
		}
}

void dp() {
	static int q[maxst];
	static bool iq[maxn][maxn];
	int hd(0), tl(1);
	memset(iq, 0, sizeof(iq));
	memset(f, 0, sizeof(f));
	memset(g, 0, sizeof(g));
	memset(ind, 0, sizeof(ind));
	ans = 0;
	q[0] = mkword(st, te);
	iq[st][te] = 1;
	while (hd < tl) {
		int i(hiword(q[hd])), j(loword(q[hd])), i0, j0;
		++ hd;
		if (t[t[i][j]][j] != j) {
			for (edge* e = head[j]; e; e = e-> next) {
				i0 = t[t[i][j]][j];
				j0 = e-> t;
				++ ind[i0][j0];
				int zn(mkword(i0, j0));
				if (!iq[i0][j0]) {
					q[tl ++] = zn;
					iq[i0][j0] = 1;
				}
			}
			i0 = t[t[i][j]][j];
			j0 = j;
			++ ind[i0][j0];
			int zn(mkword(i0, j0));
			if (!iq[i0][j0]) {
				q[tl ++] = zn;
				iq[i0][j0] = 1;
			}
		}
	}
	memset(f, 0, sizeof(f));
	memset(g, 0, sizeof(g));
	g[st][te] = 1;
	ans = 0;
	q[0] = mkword(st, te);
	hd = 0;
	tl = 1;
	while (hd < tl) {
		int i(hiword(q[hd])), j(loword(q[hd])), i0, j0;
		++ hd;
		if (i == j) {
			ans += f[i][j];
		}
		else if (t[t[i][j]][j] == j) {
			ans += f[i][j] + g[i][j];
		}
		else {
			for (edge* e = head[j]; e; e = e-> next) {
				i0 = t[t[i][j]][j];
				j0 = e-> t;
				g[i0][j0] += g[i][j] * c[j];
				f[i0][j0] += (f[i][j] + g[i][j]) * c[j];
				int zn(mkword(i0, j0));
				-- ind[i0][j0];
				if (!ind[i0][j0])
					q[tl ++] = zn;
			}
			i0 = t[t[i][j]][j];
			j0 = j;
			g[i0][j0] += g[i][j] * c[j];
			f[i0][j0] += (f[i][j] + g[i][j]) * c[j];
			int zn(mkword(i0, j0));
			-- ind[i0][j0];
			if (!ind[i0][j0])
				q[tl ++] = zn;
		}
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d%d%d", &n, &m, &st, &te);
	memset(d, 0x3f, sizeof(d));
	memset(deg, 0, sizeof(deg));
	for (int i = 0; i < m; ++ i) {
		int u, v;
		scanf("%d%d", &u, &v);
		addEdge(u, v);
		addEdge(v, u);
		++ deg[u];
		++ deg[v];
	}
	for (int i = 1; i <= n; ++ i) {
		bfs(i);
		c[i] = 1.0 / (deg[i] + 1);
	}
	dp();
	printf("%.3lf\n", ans);
}

BZOJ3052 [wc2013]糖果公园

盯idy发现的题,之前想学然后放弃了,于是今天几乎写了一天。有种紧迫感啊。然后发现好多认识的人都是最近过的。

 

思路比较简单,树上莫队。因为要修改,所以要把块的大小变成n2/3,然后用1086的方法分块。好像也可以直接按dfs序分块。两个端点和时间为三个关键字跑莫队。复杂度可以感受一下反正我也不会证。

 

要注意千万要控制LCA的用量,必需只能是O(n)级别的。在爬树的时候必需O(1)。我就是在移动时间的时候不小心手贱每次用一遍LCA,然后去检查在移动端点花了好久的时间。

 

比较不开心的是发现mac没法改栈空间?有两个点始终暴栈。而且拿另一台电脑的openSUSE跑,结果发现它的cpu没有mac好,跑得还比mac快!我猜是操作系统在捣鬼。mac还是比较坑啊不开心。然后在bzoj上跑的时间几乎是本地的5倍也是比较厉害啊。

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 100009;
const int maxl = 19;

typedef struct node {
	int t;
	node *next;
} edge;
node nlst[maxn], *np(nlst);
struct chain {
	int sz;
	node *hd, *tl;
	chain() {
		hd = tl = 0;
		sz = 0;
	}
	inline void clear() {
		hd = tl = 0;
		sz = 0;
	}
	inline void push(int x) {
		np-> t = x;
		np-> next = 0;
		if (sz) {
			tl-> next = np;
			tl = np;
		}
		else {
			hd = np;
			tl = np;
		}
		++ np;
		++ sz;
	}
	inline void merge(chain a) {
		sz += a. sz;
		if (a. sz) {
			if (hd) {
				tl-> next = a. hd;
				tl = a. tl;
			}
			else {
				hd = a. hd;
				tl = a. tl;
			}
		}
	}
};
struct query {
	int a, b, i, pa, pb, pi;
};
inline bool operator <(const query& a, const query& b) {
	return (a. pa < b. pa) || (a. pa == b. pa && a. pb < b. pb) || (a. pa == b. pa && a. pb == b. pb && a. i < b. i);
}

typedef long long dint;
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif

int n, m, q, tq, tc, ty[maxn], fb[maxn], tb, bsz;
int d[maxn], ath[maxn][maxl];
bool oc[maxn];
dint w[maxn], v[maxn], ans[maxn], cnt[maxn];
edge elst[maxn << 1], *ep(elst), *head[maxn];
query qr[maxn], cg[maxn];

inline void addEdge(int u, int v) {
	ep-> t = v;
	ep-> next = head[u];
	head[u] = ep ++;
}

chain dfs(int p) {
	chain w;
	for (int i = 1; i < maxl; ++ i)
		ath[p][i] = ath[ath[p][i - 1]][i - 1];
	for (edge* e = head[p]; e; e = e-> next)
		if (!d[e-> t]) {
			d[e-> t] = d[p] + 1;
			ath[e-> t][0] = p;
			chain g(dfs(e-> t));
			w. merge(g);
			if (w. sz >= bsz) {
				++ tb;
				for (node* it = w. hd; it; it = it-> next)
					fb[it-> t] = tb;
				w. clear();
			}
		}
	w. push(p);
	return w;
}
void divide() {
	memset(d, 0, sizeof(d));
	tb = 0;
	d[1] = 1;
	chain g(dfs(1));
	for (node* it = g. hd; it; it = it-> next)
		fb[it-> t] = tb;
}

int LCA(int u, int v) {
	if (d[u] < d[v])
		swap(u, v);
	for (int i = maxl - 1; i >= 0; -- i)
		if (d[ath[u][i]] >= d[v])
			u = ath[u][i];
	if (u == v)
		return u;
	for (int i = maxl - 1; i >= 0; -- i)
		if (ath[u][i] ^ ath[v][i]) {
			u = ath[u][i];
			v = ath[v][i];
		}
	return ath[u][0];
}
bool onChain(int u, int v, int p) {
	if (p == u || p == v)
		return 1;
	int a(LCA(u, v));
	if (p == a)
		return 1;
	if (LCA(p, a) != a)
		return 0;
	else
		return LCA(p, u) == p || LCA(p, v) == p;
}

inline void rmVertex(int p, dint& ans) {
	if (oc[p]) {
		ans -= w[cnt[ty[p]]] * v[ty[p]];
		-- cnt[ty[p]];
		ans += w[cnt[ty[p]]] * v[ty[p]];
		oc[p] = 0;
	}
}
inline void insVertex(int p, dint& ans) {
	if (!oc[p]) {
		ans -= w[cnt[ty[p]]] * v[ty[p]];
		++ cnt[ty[p]];
		ans += w[cnt[ty[p]]] * v[ty[p]];
		oc[p] = 1;
	}
}
void moveVertex(int& u, int v, int g, dint& ans) {
	int p(u), q(g), a(LCA(u, v)), t(LCA(a, g));
	if (t == a) {
		int x(LCA(u, g)), y(LCA(v, g));
		if (d[x] > d[y])
			t = x;
		else
			t = y;
		if (LCA(u, t) == t) {
			for (; q ^ t; q = ath[q][0])
				insVertex(q, ans);
			for (; p ^ t; p = ath[p][0])
				rmVertex(p, ans);
		}
		else {
			for (; q ^ t; q = ath[q][0])
				insVertex(q, ans);
			for (; p ^ a; p = ath[p][0])
				rmVertex(p, ans);
			rmVertex(a, ans);
			for (q = ath[t][0]; q ^ a; q = ath[q][0])
				rmVertex(q, ans);
		}
	}
	else {
		for (; p ^ a; p = ath[p][0])
			rmVertex(p, ans);
		for (p = ath[a][0]; p ^ t; p = ath[p][0])
			insVertex(p, ans);
		for (; q ^ t; q = ath[q][0])
			insVertex(q, ans);
		insVertex(t, ans);
	}
	u = g;
}

inline void chgType(int p, int tn, dint& ans) {
	if (oc[p]) {
		ans -= :: v[ty[p]] * w[cnt[ty[p]]];
		-- cnt[ty[p]];
		ans += :: v[ty[p]] * w[cnt[ty[p]]];
		ty[p] = tn;
		ans -= :: v[ty[p]] * w[cnt[ty[p]]];
		++ cnt[ty[p]];
		ans += :: v[ty[p]] * w[cnt[ty[p]]];
	}
	else {
		ty[p] = tn;
	}
}
void moveChgTime(int& t, int g, dint& ans) {
	for (; t + 1 < tc && cg[t + 1]. i < g; ++ t)
		chgType(cg[t + 1]. a, cg[t + 1]. b, ans);
	for (; t > -1 && cg[t]. i > g; -- t)
		chgType(cg[t]. a, cg[t]. pb, ans);
}

void cptMo() {
	memset(ans, -1, sizeof(ans));
	sort(qr, qr + tq);
	int u(1), v(1), t(-1);
	dint cans(:: v[ty[1]] * w[1]);
	memset(cnt, 0, sizeof(cnt));
	memset(oc, 0, sizeof(oc));
	oc[1] = 1;
	cnt[ty[1]] = 1;
	for (int i = 0; i < tq; ++ i) {
		moveVertex(u, v, qr[i]. a, cans);
		moveVertex(v, u, qr[i]. b, cans);
		moveChgTime(t, qr[i]. i, cans);
		ans[qr[i]. i] = cans;
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d%d", &n, &m, &q);
	bsz = (int)pow(n, 0.66) + 1;
	for (int i = 1; i <= m; ++ i)
		scanf(lld, v + i);
	w[0] = 0;
	for (int i = 1; i <= n; ++ i) {
		scanf(lld, w + i);
		w[i] += w[i - 1];
	}
	memset(head, 0, sizeof(head));
	for (int i = 1; i < n; ++ i) {
		int u, v;
		scanf("%d%d", &u, &v);
		addEdge(u, v);
		addEdge(v, u);
	}
	for (int i = 1; i <= n; ++ i)
		scanf("%d", ty + i);
	divide();
	tc = tq = 0;
	for (int i = 0; i < q; ++ i) {
		int t, a, b;
		scanf("%d%d%d", &t, &a, &b);
		if (!t) {
			cg[tc]. i = i;
			cg[tc]. a = a;
			cg[tc]. b = b
			cg[tc]. pb = ty[a];
			ty[a] = b;
			++ tc;
		}
		else {
			if (a > b)
				swap(a, b);
			qr[tq]. i = i;
			qr[tq]. pi = i / bsz;
			qr[tq]. a = a;
			qr[tq]. pa = fb[a];
			qr[tq]. b = b;
			qr[tq]. pb = fb[b];
			++ tq;
		}
	}
	for (int i = tc - 1; i >= 0; -- i)
		ty[cg[i]. a] = cg[i]. pb;
	cptMo();
	for (int i = 0; i < q; ++ i)
		if (ans[i] > -1)
			printf(lld "\n", ans[i]);
}

BZOJ2916 [Poi1997]Monochromatic Triangles

每日一水。不过这题还比较有意思。最初没看清题,觉得没法数总三角形个数。

 

同色=总-不同色。对于一个不同色,会有两个顶点的两边颜色不同。直接按照顶点统计一下不同色的边组数,然后减一下就完了。

 

说好的糖果呢。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 1009;

int n, m, t, c, w[maxn];

int main() {
#ifndef ONLINE_JUDGE
	//freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	memset(w, 0, sizeof(w));
	t = n * (n - 1) * (n - 2) / 6;
	for (int i = 0; i < m; ++ i) {
		int u, v;
		scanf("%d%d", &u, &v);
		++ w[u];
		++ w[v];
	}
	c = 0;
	for (int i = 1; i <= n; ++ i)
		c += w[i] * (n - w[i] - 1);
	c >>= 1;
	printf("%d\n", t - c);
}

BZOJ1180 [CROATIAN2009]OTOCI & BZOJ2843 极地旅行社

难得的水水的双倍题。

 

看上去是想考lct然后忘强制在线了么?那就直接预处理出形态然后链剖呗。当然我还没有达到mhy这种认为lct比tcd好写的水平。

 

中间还把修改写挂了一次,然后1180的询问数是2843的3倍又re了一次。晕。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct edge {
	int t;
	edge *next;
};
struct seg {
	int l, r, s[2];
	seg *ls, *rs;
};
struct query {
	char o;
	int a, b, s;
};

const int maxn = 30009;
const int maxm = 300009;

int n, m, r[maxn], d[maxn], v[maxn], u[maxn];
int fa[maxn], sz[maxn], tc, fc[maxn], ch[maxn], cl[maxn];
int qret[2];
seg slst[maxn << 2], *sp(slst), *rt[maxn];
edge elst[maxn << 1], *ep(elst), *head[maxn];
query q[maxm];

inline void addEdge(int u, int v) {
	ep-> t = v;
	ep-> next = head[u];
	head[u] = ep ++;
}

int getRoot(int x) {
	register int p, q;
	for (p = r[x]; r[p] ^ p; p = r[p]);
	for (; x ^ p; q = r[x], r[x] = p, x = q);
	return x;
}

void divide(int p0) {
	static int q[maxn];
	int hd(0), tl(1);
	q[0] = p0;
	d[p0] = 1;
	fa[p0] = 0;
	while (hd < tl) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (e-> t != fa[p]) {
				d[e-> t] = d[p] + 1;
				fa[e-> t] = p;
				q[tl ++] = e-> t;
			}
	}
	for (int i = tl - 1; i >= 0; -- i) {
		int p(q[i]), z(-1);
		sz[p] = 1;
		for (edge* e = head[p]; e; e = e-> next)
			if (e-> t != fa[p]) {
				sz[p] += sz[e-> t];
				if (z == -1 || sz[e-> t] > sz[z])
					z = e-> t;
			}
		if (z == -1)
			cl[fc[p] = ++ tc] = 1;
		else
			++ cl[fc[p] = fc[z]];
		ch[fc[p]] = p;
	}
}

#define cpos(_p_) (d[_p_]-d[ch[fc[_p_]]])
#define midp(p) ((p->l+p->r)>>1)

inline seg* sgtBuild(int l, int r) {
	seg* p(sp ++);
	p-> l = l;
	p-> r = r;
	p-> s[0] = r - l;
	p-> s[1] = 0;
	if (l + 1 < r) {
		p-> ls = sgtBuild(l, midp(p));
		p-> rs = sgtBuild(midp(p), r);
	}
	return p;
}
inline void sgtChg(seg* p, int p0, int i, int v) {
	if (p-> l + 1 == p-> r)
		p-> s[i] = v;
	else {
		if (p0 < midp(p))
			sgtChg(p-> ls, p0, i, v);
		else
			sgtChg(p-> rs, p0, i, v);
		p-> s[i] = p-> ls-> s[i] + p-> rs-> s[i];
	}
}
inline void sgtQry(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r) {
		qret[0] += p-> s[0];
		qret[1] += p-> s[1];
	}
	else if (r <= midp(p))
		sgtQry(p-> ls, l, r);
	else if (l >= midp(p))
		sgtQry(p-> rs, l, r);
	else {
		sgtQry(p-> ls, l, midp(p));
		sgtQry(p-> rs, midp(p), r);
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i) {
		r[i] = i;
		scanf("%d", v + i);
		u[i] = 1;
	}
	scanf("%d", &m);
	for (int i = 0; i < m; ++ i) {
		char opt[11];
		scanf("%s%d%d", opt, &q[i]. a, &q[i]. b);
		q[i]. o = opt[0];
		if (q[i]. o == 'b') {
			if (getRoot(q[i]. a) ^ getRoot(q[i]. b)) {
				q[i]. s = 1;
				r[getRoot(q[i]. a)] = getRoot(q[i]. b);
				addEdge(q[i]. a, q[i]. b);
				addEdge(q[i]. b, q[i]. a);
			}
			else {
				q[i]. s = 0;
			}
		}
	}
	memset(d, 0, sizeof(d));
	tc = 0;
	for (int i = 1; i <= n; ++ i)
		if (!d[i])
			divide(i);
	for (int i = 1; i <= tc; ++ i)
		rt[i] = sgtBuild(0, cl[i]);
	for (int i = 1; i <= n; ++ i)
		sgtChg(rt[fc[i]], cpos(i), 1, v[i]);
	for (int i = 0; i < m; ++ i)
		if (q[i]. o == 'b') {
			if (q[i]. s) {
				if (d[q[i]. a] > d[q[i]. b])
					swap(q[i]. a, q[i]. b);
				u[q[i]. b] = 0;
				sgtChg(rt[fc[q[i]. b]], cpos(q[i]. b), 0, 0);
				puts("yes");
			}
			else
				puts("no");
		}
		else if (q[i]. o == 'p') {
			sgtChg(rt[fc[q[i]. a]], cpos(q[i]. a), 1, q[i]. b);
		}
		else if (q[i]. o == 'e') {
			int u(q[i]. a), v(q[i]. b);
			if (getRoot(u) != getRoot(v)) {
				puts("impossible");
			}
			else {
				qret[0] = qret[1] = 0;
				while (fc[u] ^ fc[v]) {
					if (d[ch[fc[u]]] > d[ch[fc[v]]]) {
						sgtQry(rt[fc[u]], 0, cpos(u) + 1);
						u = fa[ch[fc[u]]];
					}
					else {
						sgtQry(rt[fc[v]], 0, cpos(v) + 1);
						v = fa[ch[fc[v]]];
					}
				}
				if (d[u] > d[v])
					swap(u, v);
				sgtQry(rt[fc[u]], cpos(u), cpos(v) + 1);
				qret[0] -= :: u[u];
				if (qret[0])
					puts("impossible");
				else
					printf("%d\n",  qret[1]);
			}
		}
}

BZOJ1822 [JSOI2010]Frozen Nova 冷冻波

简单的算几+网络流么。然后花了将近两天。最近总和算几过不去啊。

 

判断线段和圆是否有交还是比较简单的。反正不要让我用解析。先找下垂线,然后找下垂足,然后点乘一下搞定。然后错得不知道怎么死的。搞了很久。

 

然后二分网络流水水水水水水水水。

 

感觉mac好卡啊,gdb巨卡,bash也非常卡。难道我又没有搞对打开方式?

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

inline long double sqr(long double x) {
	return x * x;
}

typedef struct geo_obj {
	double x, y;
	geo_obj() {}
	geo_obj(double x0, double y0) {
		x = x0, y = y0;
	}
	inline void read() {
		scanf("%lf%lf", &x, &y);
	}
	inline geo_obj rev() {
		return geo_obj(-y, x);
	}
	inline double len() {
		return sqrt(sqr(x) + sqr(y));
	}
	inline double sqLen() {
		return sqr(x) + sqr(y);
	}
} point, vect;
inline geo_obj operator +(const geo_obj& a, const geo_obj& b) {
	return geo_obj(a. x + b. x, a. y + b. y);
}
inline geo_obj operator -(const geo_obj& a, const geo_obj& b) {
	return geo_obj(a. x - b. x, a. y - b. y);
}
inline geo_obj operator *(const geo_obj& a, const double& b) {
	return geo_obj(a. x * b, a. y * b);
}
inline double operator *(const geo_obj& a, const geo_obj& b) {
	return a. x * b. y - a. y * b. x;
}
inline double operator &(const geo_obj& a, const geo_obj& b) {
	return a. x * b. x + a. y * b. y;
}

struct line {
	point p;
	vect v;
	line(point a, point b) {
		p = a;
		v = b - a;
	}
};
struct circle {
	point o;
	double r;
	void read() {
		o. read();
		scanf("%lf", &r);
	}
};

inline point lineCross(const line& a, const line& b) {
	vect w(b. p - a. p);
	double rat((w * a. v) / (a. v * b. v));
	return b. p + b. v * rat;
}

bool segInCircle(point a, point b, circle c) {
	line l(a, b);
	vect u(l. v. rev());
	line r(c. o, c. o + u);
	point p(lineCross(l, r));
	double x = (p - c. o). sqLen();
	double sr(sqr(c. r));
	if (x >= sr)
		return 0;
	if (((a - p) & (b - p)) <= 0)
		return 1;
	if ((a - c. o). sqLen() <= sr)
		return 1;
	if ((b - c. o). sqLen() <= sr)
		return 1;
	return 0;
}

struct edge {
	int t, c;
	edge *next, *rv;
};

struct wiz {
	point p;
	int r, t;
	void read() {
		p. read();
		scanf("%d%d", &r, &t);
	}
};

const int maxn = 409;
const int maxe = 80409;
const int inf = 0x3f3f3f3f;

int n, m, c, t, ea[maxe], eb[maxe], ou[maxn];
int d[maxn], st, te;
wiz w[maxn];
circle tree[maxn];
point spi[maxn];
edge elst[maxe], *ep, *head[maxn << 1];

inline edge* newEdge(int u, int v, int c) {
	ep-> t = v;
	ep-> c = c;
	ep-> next = head[u];
	head[u] = ep;
	return ep ++;
}
inline void addEdge(int u, int v, int c) {
	edge *a = newEdge(u, v, c);
	edge *b = newEdge(v, u, 0);
	a-> rv = b;
	b-> rv = a;
}

bool bfsBuild() {
	static int q[maxn];
	int hd(0), tl(1);
	memset(d, 0, sizeof(d));
	d[q[0] = st] = 1;
	while (hd < tl && !d[te]) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (e-> c && !d[e-> t]) {
				d[e-> t] = d[p] + 1;
				q[tl ++] = e-> t;
			}
	}
	return d[te];
}
int dfsFind(int p, int c) {
	if (p == te)
		return c;
	int s(0);
	edge* e;
	for (e = head[p]; e && c; e = e-> next)
		if (e-> c && d[e-> t] == d[p] + 1) {
			int x(dfsFind(e-> t, min(e-> c, c)));
			s += x;
			c -= x;
			e-> c -= x;
			e-> rv-> c += x;
		}
	if (!e)
		d[p] = -1;
	return s;
}

int binSearch() {
	int l(0), r(5000000);
	st = n + m;
	te = st + 1;
	while (l < r) {
		int mid((l + r) >> 1);
		ep = elst;
		memset(head, 0, sizeof(head));
		for (int i = 0; i < t; ++ i)
			addEdge(ea[i], eb[i] + n, 1);
		for (int i = 0; i < n; ++ i)
			addEdge(st, i, (mid + w[i]. t) / w[i]. t);
		for (int i = 0; i < m; ++ i)
			addEdge(i + n, te, 1);
		int s(0);
		while (bfsBuild())
			s += dfsFind(st, inf);
		if (s == m)
			r = mid;
		else
			l = mid + 1;
	}
	return l;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d%d", &n, &m, &c);
	for (int i = 0; i < n; ++ i)
		w[i]. read();
	for (int i = 0; i < m; ++ i)
		spi[i]. read();
	for (int i = 0; i < c; ++ i)
		tree[i]. read();
	memset(ou, 0, sizeof(ou));
	t = 0;
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < m; ++ j) {
			bool f(1);
			double ds(sqr(w[i]. p. x - spi[j]. x) + sqr(w[i]. p. y - spi[j]. y));
			if (ds > sqr(w[i]. r))
				f = 0;
			for (int k = 0; k < c && f; ++ k)
				if (segInCircle(w[i]. p, spi[j], tree[k]))
					f = 0;
			if (f) {
				ea[t] = i;
				eb[t] = j;
				++ t;
				ou[j] = 1;
			}
		}
	bool hs(1);
	for (int i = 0; i < m && hs; ++ i)
		if (!ou[i])
			hs = 0;
	if (!hs || !n)
		puts("-1");
	else
		printf("%d\n", binSearch());
}

BZOJ2157 旅游

去找水题于是找到这么一道恶心的代码题。其实应该可以用函数指针来优化一下代码量,不过懒得了。虽然复制粘贴导致调试了老半天。

 

水水的链剖+线段树。然后tag打一打就好了。

 

最近是装备了高超的开坑技巧啊。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct seg {
	int l, r, s, a, b, rv;
	seg *ls, *rs;
	inline void update() {
		if (l + 1 < r) {
			s = ls-> s + rs-> s;
			a = min(ls-> a, rs-> a);
			b = max(ls-> b, rs-> b);
			if (rv) {
				s = -s;
				swap(a, b);
				a = -a;
				b = -b;
			}
		}
		else
			a = b = s;
	}
	inline void chgTag() {
		s = -s;
		swap(a, b);
		a = -a;
		b = -b;
		rv ^= 1;
	}
	inline void downTag() {
		if (rv) {
			rv = 0;
			if (l + 1 < r) {
				ls-> chgTag();
				rs-> chgTag();
			}
		}
	}
};
struct edge {
	int t, v;
	edge *next;
};

const int maxn = 20009;

int n, m, fa[maxn], d[maxn], vl[maxn], ea[maxn], eb[maxn];
int sz[maxn], tc, fc[maxn], ch[maxn], cl[maxn];
edge elst[maxn << 1], *ep(elst), *head[maxn];
seg slst[maxn << 2], *sp(slst), *rt[maxn];

inline void addEdge(int u, int v, int w) {
	ep-> t = v;
	ep-> v = w;
	ep-> next = head[u];
	head[u] = ep ++;
}

#define midp(p) ((p->l+p->r)>>1)
inline seg* sgtBuild(int l, int r) {
	seg* p(sp ++);
	p-> l = l;
	p-> r = r;
	if (l + 1 < r) {
		p-> ls = sgtBuild(l, midp(p));
		p-> rs = sgtBuild(midp(p), r);
	}
	return p;
}
inline void sgtChg(seg* p, int p0, int v0) {
	if (p-> l + 1 == p-> r) {
		p-> rv = 0;
		p-> s = v0;
	}
	else {
		p-> downTag();
		if (p0 < midp(p))
			sgtChg(p-> ls, p0, v0);
		else
			sgtChg(p-> rs, p0, v0);
	}
	p-> update();
}
inline int sgtSum(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r)
		return p-> s;
	else {
	   p-> downTag();
	   if (r <= midp(p))
		   return sgtSum(p-> ls, l, r);
	   else if (l >= midp(p))
		   return sgtSum(p-> rs, l, r);
	   else
		   return sgtSum(p-> ls, l, midp(p)) + sgtSum(p-> rs, midp(p), r);
	}
}
inline int sgtMin(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r)
		return p-> a;
	else {
		p-> downTag();
		if (r <= midp(p))
			return sgtMin(p-> ls, l, r);
		else if (l >= midp(p))
			return sgtMin(p-> rs, l, r);
		else
			return min(sgtMin(p-> ls, l, midp(p)), sgtMin(p-> rs, midp(p), r));
	}
}
inline int sgtMax(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r)
		return p-> b;
	else {
		p-> downTag();
		if (r <= midp(p))
			return sgtMax(p-> ls, l, r);
		else if (l >= midp(p))
			return sgtMax(p-> rs, l, r);
		else
			return max(sgtMax(p-> ls, l, midp(p)), sgtMax(p-> rs, midp(p), r));
	}
}
inline void sgtRev(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r)
		p-> chgTag();
	else {
		p-> downTag();
		if (r <= midp(p))
			sgtRev(p-> ls, l, r);
		else if (l >= midp(p))
			sgtRev(p-> rs, l, r);
		else
			sgtRev(p-> ls, l, midp(p)), sgtRev(p-> rs, midp(p), r);
	}
	p-> update();
}

#define cpos(_p_) (d[_p_]-d[ch[fc[_p_]]])

void divide() {
	static int q[maxn];
	int hd(0), tl(1);
	q[0] = 0;
	memset(d, 0, sizeof(d));
	d[0] = 1;
	fa[0] = 0;
	vl[0] = 0;
	while (hd < tl) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (!d[e-> t]) {
				fa[e-> t] = p;
				d[e-> t] = d[p] + 1;
				q[tl ++] = e-> t;
				vl[e-> t] = e-> v;
			}
	}
	tc = 0;
	for (int ti = tl - 1; ti >= 0; -- ti) {
		int p(q[ti]), z(-1);
		sz[p] = 1;
		for (edge* e = head[p]; e; e = e-> next)
			if (e-> t != fa[p]) {
				sz[p] += sz[e-> t];
				if (z == -1 || sz[e-> t] > sz[z])
					z = e-> t;
			}
		if (z == -1)
			cl[fc[p] = ++ tc] = 1;
		else
			++ cl[fc[p] = fc[z]];
		ch[fc[p]] = p;
	}
	for (int i = 1; i <= tc; ++ i)
		rt[i] = sgtBuild(0, cl[i]);
	for (int i = 0; i < n; ++ i)
		sgtChg(rt[fc[i]], cpos(i), vl[i]);
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	memset(head, 0, sizeof(d));
	for (int i = 1; i < n; ++ i) {
		int w;
		scanf("%d%d%d", ea + i, eb + i, &w);
		addEdge(ea[i], eb[i], w);
		addEdge(eb[i], ea[i], w);
	}
	divide();
	scanf("%d", &m);
	while (m --) {
		char opt[7];
		int u, v, w;
		scanf("%s", opt);
		if (opt[0] == 'C') {
			scanf("%d%d", &u, &w);
			if (d[ea[u]] > d[eb[u]])
				swap(ea[u], eb[u]);
			v = eb[u];
			sgtChg(rt[fc[v]], cpos(v), w);
		}
		else if (opt[0] == 'N') {
			scanf("%d%d", &u, &v);
			while (fc[u] ^ fc[v])
				if (d[ch[fc[u]]] > d[ch[fc[v]]]) {
					sgtRev(rt[fc[u]], 0, cpos(u) + 1);
					u = fa[ch[fc[u]]];
				}
				else {
					sgtRev(rt[fc[v]], 0, cpos(v) + 1);
					v = fa[ch[fc[v]]];
				}
			if (d[u] > d[v])
				swap(u, v);
			if (u ^ v)
				sgtRev(rt[fc[u]], cpos(u) + 1, cpos(v) + 1);
		}
		else if (opt[0] == 'S') {
			int s(0);
			scanf("%d%d", &u, &v);
			while (fc[u] ^ fc[v])
				if (d[ch[fc[u]]] > d[ch[fc[v]]]) {
					s += sgtSum(rt[fc[u]], 0, cpos(u) + 1);
					u = fa[ch[fc[u]]];
				}
				else {
					s += sgtSum(rt[fc[v]], 0, cpos(v) + 1);
					v = fa[ch[fc[v]]];
				}
			if (d[u] > d[v])
				swap(u, v);
			if (u ^ v)
				s += sgtSum(rt[fc[u]], cpos(u) + 1, cpos(v) + 1);
			printf("%d\n", s);
		}
		else if (opt[1] == 'I') {
			int s(0x3f3f3f3f);
			scanf("%d%d", &u, &v);
			while (fc[u] ^ fc[v])
				if (d[ch[fc[u]]] > d[ch[fc[v]]]) {
					s = min(s, sgtMin(rt[fc[u]], 0, cpos(u) + 1));
					u = fa[ch[fc[u]]];
				}
				else {
					s = min(s, sgtMin(rt[fc[v]], 0, cpos(v) + 1));
					v = fa[ch[fc[v]]];
				}
			if (d[u] > d[v])
				swap(u, v);
			if (u ^ v)
				s = min(s, sgtMin(rt[fc[u]], cpos(u) + 1, cpos(v) + 1));
			printf("%d\n", s);
		}
		else if (opt[1] == 'A') {
			int s(-0x3f3f3f3f);
			scanf("%d%d", &u, &v);
			while (fc[u] ^ fc[v])
				if (d[ch[fc[u]]] > d[ch[fc[v]]]) {
					s = max(s, sgtMax(rt[fc[u]], 0, cpos(u) + 1));
					u = fa[ch[fc[u]]];
				}
				else {
					s = max(s, sgtMax(rt[fc[v]], 0, cpos(v) + 1));
					v = fa[ch[fc[v]]];
				}
			if (d[u] > d[v])
				swap(u, v);
			if (u ^ v)
				s = max(s, sgtMax(rt[fc[u]], cpos(u) + 1, cpos(v) + 1));
			printf("%d\n", s);
		}
	}
}

BZOJ1845 [Cqoi2005] 三角形面积并

比较基础的算几。当是hwd在wc的时候讲的东西拿来练练吧。(都过去多久了)

 

算几的板比较长。然后记得还有道半平面交精度题至今没过!?什么效率。

 

这题的思路比较简单。找出所有的关键x,分段。每段内都是一堆边不相交的梯形,那么答案=∑(▵x*∑中位线并的长度)。总时间复杂度到了O(n3)。不过n才100显然可过。至于n等于1000的题暂时不会啊。我太弱了。

 

然后这题也有神奇的精度误差,答案要-1e-7才能过。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const double eps = 1e-7;
inline int sgn(double x) {
	return (x > eps) - (x < -eps);
}
inline double sqr(double x) {
	return x * x;
}

typedef struct geo_obj {
	double x, y;
	inline geo_obj() {}
	inline geo_obj(double x0, double y0) {
		x = x0, y = y0;
	}
	inline void read() {
		scanf("%lf%lf", &x, &y);
	}
} point, vect;
inline geo_obj operator +(const geo_obj& a, const geo_obj& b) {
	return geo_obj(a. x + b. x, a. y + b. y);
}
inline geo_obj operator -(const geo_obj& a, const geo_obj& b) {
	return geo_obj(a. x - b. x, a. y - b. y);
}
inline geo_obj operator *(const geo_obj& a, const double& b) {
	return geo_obj(a. x * b, a. y * b);
}
inline double operator *(const geo_obj& a, const geo_obj& b) {
	return a. x * b. y - a. y * b. x;
}
inline double operator &(const geo_obj& a, const geo_obj& b) {
	return a. x * b. x + a. y * b. y;
}
inline bool operator <(const geo_obj& a, const geo_obj& b) {
	return (a. x < b. x) || (a. x == b. x && a. y < b. y);
}

struct line {
	point p;
	vect v;
	inline line() {}
	inline line(const point& a, point& b) {
		p = a, v = b - a;
	}
};
struct triangle {
	point a[3];
	inline triangle() {}
	inline void read() {
		for (int i = 0; i < 3; ++ i)
			a[i]. read();
		if (sgn((a[1] - a[0]) * (a[2] - a[0])) < 0)
			swap(a[1], a[2]);
	}
};

typedef pair <double, int> ypr;

const int maxn = 109;
const int maxp = 90009;
const int ara[3] = {0, 1, 2}, arb[3] = {1, 2, 0};

int n, t, c;
triangle a[maxn];
double x[maxp], ans(0);
ypr y[maxp];

point lineCross(line a, line b) {
	vect w(b. p - a. p);
	double rat((w * a. v) / (a. v * b. v));
	return b. p + b. v * rat;
}

void addX(point a, point b, point c, point d) {
	if (!sgn((b - a) * (d - c)))
		return;
	point e(lineCross(line(a, b), line(c, d)));
	if (((a - e) & (b - e)) > 0 || ((c - e) & (d - e)) > 0)
		return;
	x[t ++] = e. x;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	for (int i = 0; i < n; ++ i)
		a[i]. read();
	t = 0;
	for (int i = 0; i < n; ++ i) {
		for (int j = 0; j < 3; ++ j)
			x[t ++] = a[i]. a[j]. x;
		for (int j = 0; j < i; ++ j)
			for (int k = 0; k < 3; ++ k)
				for (int l = 0; l < 3; ++ l)
					addX(a[i]. a[ara[k]], a[i]. a[arb[k]], a[j]. a[ara[l]], a[j]. a[arb[l]]);
	}
	sort(x, x + t);
	t = unique(x, x + t) - x;
	for (int i = 0; i < t - 1; ++ i) {
		double xm((x[i] + x[i + 1]) / 2.0);
		c = 0;
		for (int j = 0; j < n; ++ j)
			for (int k = 0; k < 3; ++ k) {
				point u(a[j]. a[ara[k]]), v(a[j]. a[arb[k]]);
				if ((xm - u. x) * (xm - v. x) < 0) {
					point w(u + (v - u) * ((xm - u. x) / (v. x - u. x)));
					y[c ++] = ypr(w. y, (u. x < v. x) ? 1 : -1);
				}
			}
		sort(y, y + c);
		int cnt(0);
		double len(0);
		for (int i = 0; i < c - 1; ++ i) {
			cnt += y[i]. second;
			if (cnt)
				len += (y[i + 1]. first - y[i]. first);
		}
		ans += len * (x[i + 1] - x[i]);
	}
	printf("%.2lf\n", ans - eps);
}

BZOJ2342 [Shoi2011]双倍回文

久了不写manacher又手生了只好抄红书了。

 

后面的过程就是对每个位置i找一个最远的位置j满足j在i为中心的回文子串的一半以内且j最小。然后直接拿个set就能搞定。

 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>

using namespace std;

const int maxn = 500009;

char a[maxn];
int d[maxn << 1], n;

void manacher() {
	d[0] = 1;
	for (int i = 1, j = 0; i < (n << 1) - 1; ++ i) {
		int p(i >> 1), q(i - p), r(((j + 1) >> 1) + d[j] - 1);
		if (r < q)
			d[i] = 0;
		else
			d[i] = min(r - q + 1, d[(j << 1) - i]);
		while (p - d[i] >= 0 && q + d[i] < n && a[p - d[i]] == a[q + d[i]])
			++ d[i];
		if (q + d[i] - 1 > r)
			j = i;
	}
}

int pr[maxn];
inline int getr(int _x_) {
	return _x_+d[((_x_)<<1)+1]; 
}
inline bool cmpRight(const int& a, const int& b) {
	return getr(a) < getr(b);
}

set <int> rg;
int sov() {
	int ans(0);
	for (int i = 0; i < n - 1; ++ i)
		pr[i] = i;
	sort(pr, pr + n - 1, cmpRight);
	for (int i = 0, k = 0; k < n; ++ k) {
		for (; i < n - 1 && getr(pr[i]) < k; ++ i)
			rg. erase(pr[i]);
		set <int> :: iterator it = rg. lower_bound(k - (d[(k << 1) + 1] >> 1));
		if (it != rg. end())
			ans = max(ans, k - *it);
		rg. insert(k);
	}
	return ans * 4;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%s", &n, a);
	manacher();
	printf("%d\n", sov());
}

BZOJ1040 [ZJOI2008]骑士

多少年前的坑了。

 

用mac写的第一篇题解呢。

 

环套树DP。先把树上的情况处理了,然后枚举取不取环上的第一个。

 

要注意会有重边的情况,比较恶心。

 

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>

using namespace std;

const int buf_len = 4000;
char buf[buf_len], *bufb(buf), *bufe(buf + 1);
#define readBuf() { \
	if (++ bufb == bufe) \
		bufe = (bufb = buf) + fread(buf, 1, sizeof(buf), stdin); \
}
#define readInt(_x_) { \
	register int _s_(0); \
	do { \
		readBuf(); \
	} while (!isdigit(*bufb)); \
	do { \
		_s_ = _s_ * 10 + *bufb - 48; \
		readBuf(); \
	} while (isdigit(*bufb)); \
	_x_ = _s_; \
}

typedef long long dint;
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif

struct edge {
	int t;
	edge *next;
};

const int maxn = 1000009;

int n, v[maxn];
int stc[maxn], tst, lpp[maxn], tlp;
int vis[maxn], onl[maxn];
dint f[maxn], g[maxn];
edge elst[maxn << 1], *ep(elst), *head[maxn];

inline void addEdge(int u, int v) {
	ep-> t = v;
	ep-> next = head[u];
	head[u] = ep ++;
}

void dfsLoop(int p, int fr) {
	vis[p] = fr + 1;
	stc[tst ++] = p;
	for (edge* e = head[p]; e; e = e-> next)
		if (e-> t != fr) {
			if (!vis[e-> t]) {
				dfsLoop(e-> t, p);
			}
			else if (vis[e-> t] && vis[e-> t] != p + 1) {
				if (onl[p])
					continue;
				tlp = 0;
				do {
					-- tst;
					lpp[tlp ++] = stc[tst];
					onl[stc[tst]] = 1;
				} while (stc[tst] != e-> t);
			}
		}
	if (stc[tst - 1] == p)
		-- tst;
}

void dfsDP(int p, int fr) {
	f[p] = 0;
	g[p] = v[p];
	vis[p] = -1;
	for (edge* e = head[p]; e; e = e-> next)
		if (e-> t != fr && !onl[e-> t] && vis[e-> t] != -1) {
			dfsDP(e-> t, p);
			f[p] += max(f[e-> t], g[e-> t]);
			g[p] += f[e-> t];
		}
}

dint calcCC(int p) {
	tst = 0;
	tlp = 0;
	dfsLoop(p, 0);
	if (!tlp)
		lpp[tlp ++] = p;
	for (int i = 0; i < tlp; ++ i)
		dfsDP(lpp[i], 0);
	dint s0(0), s1(0);
	if (tlp < 3) {
		for (int i = 0; i < tlp; ++ i)
			s0 = max(s0, g[lpp[i]] - f[lpp[i]]);
		for (int i = 0; i < tlp; ++ i)
			s0 += f[lpp[i]];
	}
	else {
		dint f0(g[lpp[0]] + f[lpp[1]] + f[lpp[tlp - 1]]), g0(f0);
		for (int i = 2; i < tlp - 1; ++ i) {
			dint f1(f[lpp[i]] + max(f0, g0)), g1(g[lpp[i]] + f0);
			f0 = f1;
			g0 = g1;
		}
		s0 = max(f0, g0);
	}
	if (tlp < 3) {
		s1 = s0;
	}
	else {
		dint f0(f[lpp[0]]), g0(f0);
		for (int i = 1; i < tlp; ++ i) {
			dint f1(f[lpp[i]] + max(f0, g0)), g1(g[lpp[i]] + f0);
			f0 = f1;
			g0 = g1;
		}
		s1 = max(g0, f0);
	}
	return max(s0, s1);
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	readInt(n);
	memset(head, 0, sizeof(head));
	for (int i = 1; i <= n; ++ i) {
		int x;
		readInt(v[i]);
		readInt(x);
		addEdge(i, x);
		addEdge(x, i);
	}
	memset(vis, 0, sizeof(vis));
	memset(onl, 0, sizeof(onl));
	dint ans(0);
	for (int i = 1; i <= n; ++ i)
		if (!vis[i])
			ans += calcCC(i);
	printf(lld "\n", ans);
}

BZOJ1017 [JSOI2008]魔兽地图DotR

数据水。填坑。语文阅读题,看到都不想做。

 

就一树形dp。f[i][j][k]表示第i个物品,上供j个,花k块钱的代价。背包转移是O(m2)的,因为是一棵树所以转移次数是O(n)的,但是还要考虑j最大为100。一个技巧是合并完所有的东西之后再考虑把上供的烧掉。虽然这样的复杂度还是有点高。

 

毕竟我还是太弱啊,居然又花了一个上午。(虽然睡到十点)

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct edge {
	int t, v;
	edge *next;
};

const int maxm = 2009;
const int maxn = 53;
const int maxa = 103;

int n, m, f[maxn][maxa][maxm], ans[maxm], vl[maxn], vmax[maxn];
bool lf[maxn], ir[maxn];
edge elst[maxn], *ep(elst), *head[maxn];

inline void addEdge(int u, int v, int w) {
	ep-> t = v;
	ep-> v = w;
	ep-> next = head[u];
	head[u] = ep ++;
}

inline void upMax(int& a, int b) {
	if (a < b)
		a = b;
}

void mergeFunc(int* s, int* a, int* b) {
	static int r[maxm];
	memset(r, -1, sizeof(r));
	for (int i = 0; i <= m; ++ i)
		if (a[i] > -1)
			for (int j = 0; j + i <= m; ++ j)
				if (b[j] > -1)
					upMax(r[i + j], a[i] + b[j]);
	for (int i = 0; i <= m; ++ i)
		s[i] = r[i];
}

void dfsVMax(int p) {
	if (lf[p])
		return;
	vmax[p] = 100;
	for (edge* e = head[p]; e; e = e-> next) {
		dfsVMax(e-> t);
		vmax[p] = min(vmax[p], vmax[e-> t] / e-> v);
	}
}

void dfsDP(int p) {
	if (lf[p])
		return;
	for (int i = 0; i <= vmax[p]; ++ i)
		f[p][i][0] = 0;
	for (edge* e = head[p]; e; e = e-> next) {
		dfsDP(e-> t);
		for (int j = 0; j <= vmax[p]; ++ j)
			mergeFunc(f[p][j], f[p][j], f[e-> t][j * e-> v]);
	}
	for (int j = 1; j <= vmax[p]; ++ j)
		for (int k = 0; k < j; ++ k)
			for (int l = 0; l <= m; ++ l)
				if (f[p][j][l] > -1)
					upMax(f[p][k][l], f[p][j][l] + vl[p] * (j - k));
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	memset(head, 0, sizeof(head));
	memset(f, -1, sizeof(f));
	memset(ir, 1, sizeof(ir));
	for (int i = 1; i <= n; ++ i) {
		char ty[3];
		scanf("%d%s", vl + i, ty);
		lf[i] = (ty[0] == 'B');
		if (lf[i]) {
			int b, c;
			scanf("%d%d", &c, &b);
			for (int j = 0; j <= b && c * j <= m; ++ j)
				for (int k = 0; k <= j; ++ k)
					upMax(f[i][k][j * c], (j - k) * vl[i]);
			vmax[i] = b;
		}
		else {
			int t, v, w;
			scanf("%d", &t);
			for (int j = 0; j < t; ++ j) {
				scanf("%d%d", &v, &w);
				addEdge(i, v, w);
				ir[v] = 0;
			}
		}
	}
	for (int i = 1; i <= n; ++ i)
		if (ir[i])
			dfsVMax(i);
	memset(ans, -1, sizeof(ans));
	ans[0] = 0;
	for (int i = 1; i <= n; ++ i)
		if (ir[i]) {
			dfsDP(i);
			mergeFunc(ans, ans, f[i][0]);
		}
	int s(0);
	for (int i = 0; i <= m; ++ i)
		upMax(s, ans[i]);
	printf("%d\n", s);
}

 

 

BZOJ2331 [SCOI2011]地板

插头DP的基础题。想插头DP都是将军没走的时候给我讲的了,距今有大半年了吧。mhy已经写了无数道了,我才开始写。唉,还是效率太低。

 

插头DP的意思是把已经处理的部分的轮廓线上对后面状态有影响的东西状压下来,然后挨着处理每个格子的状态(比如方向,或者这里的地板怎么铺之类的)。

 

具体到这道题来说,考虑一个相邻格子之前的边,那么它可能没有连接两个相同的磁砖,有可能连接了两个还没有拐过弯的磁砖,有可能连接了两个已经拐过弯的磁砖,一共是3种情况,所以用一个三进制数把它压下来。然后直接一路转移过去就行了。要注意的是每行都处理完了之后要把最边上的那一条从右移动到左,所以还需要把所有的状态都改一下。

 

今天效率比较低,心不在焉,然后在最后一个转移的地方wa了大半天。插头DP的调试也比较麻烦。所以我还是naive。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 13;
const int maxa = 109;
const int maxs = 200009;
const int mod = 20110520;

bool mp[maxa][maxa];
int n, m, a[maxn], b[maxn], f[2][maxs];

#define fc f[cur]
#define fp f[prv]
#define doNothing ;
inline void mInc(int&  _a_, int _b_) {
	_a_ += _b_;
	if (_a_ >= mod)
		_a_ %= mod;
}

void unzipStatus(int z, int* a) {
	for (int i = 0; i <= m; ++ i) {
		a[i] = z % 3;
		z /= 3;
	}
}
int zipStatus(int* a) {
	int s(0);
	for (int i = m; i >= 0; -- i)
		s = s * 3 + a[i];
	return s;
}

int plugDP() {
	int prv(0), cur(1), e(1);
	memset(f, 0, sizeof(f));
	fc[0] = 1;
	for (int i = 0; i <= m; ++ i)
		e *= 3;
	for (int i = 0; i < n; ++ i) {
		for (int j = 0; j < m; ++ j) {
			swap(cur, prv);
			for (int z = 0; z < e; ++ z)
				fc[z] = 0;
			for (int z = 0; z < e; ++ z)
				if (fp[z]) {
					unzipStatus(z, a);
					if (mp[i][j]) {
						if (a[j] == 0) {
							if (a[j + 1] == 0) {
								memcpy(b, a, sizeof(a));
								b[j] = b[j + 1] = 2;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 0;
								b[j + 1] = 1;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 1;
								b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 1) {
								memcpy(b, a, sizeof(a));
								b[j] = 0;
								b[j + 1] = 2;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 1;
								b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 2) {
								memcpy(b, a, sizeof(a));
								b[j] = 2;
								b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
						}
						else if (a[j] == 1) {
							if (a[j + 1] == 0) {
								memcpy(b, a, sizeof(a));
								b[j] = 0;
								b[j + 1] = 1;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 2;
								b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 1) {
								memcpy(b, a, sizeof(a));
								b[j] = b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 2) {
								doNothing
							}
						}
						else if (a[j] == 2) {
							if (a[j + 1] == 0) {
								memcpy(b, a, sizeof(a));
								b[j] = b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j + 1] = 2;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 1) {
								doNothing
							}
							else if (a[j + 1] == 2) {
								doNothing
							}
						}
					}
					else {
						if (a[j] == 0 && a[j + 1] == 0)
							mInc(fc[z], fp[z]);
					}
				}
		}
		swap(cur, prv);
		for (int z = 0; z < e; ++ z) 
			fc[z] = 0;
		for (int z = 0; z < e; ++ z) 
			if (fp[z]) {
				unzipStatus(z, a);
				if (a[m])
					continue;
				for (int j = m; j; -- j)
					a[j] = a[j - 1];
				a[0] = 0;
				fc[zipStatus(a)] = fp[z];
			}
	}
	return fc[0];
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	bool rev(n < m);
	for (int i = 0; i < n; ++ i) {
		static char g[maxa];
		scanf("%s", g);
		for (int j = 0; j < m; ++ j)
			if (rev)
				mp[j][i] = (g[j] == '_');
			else
				mp[i][j] = (g[j] == '_');
	}
	if (rev)
		swap(n, m);
	printf("%d\n", plugDP());
}

BZOJ3769 SPOJ8549 BST again

什么鬼。

 

很好想的dp。f[i][j]表示深度不超过i且大小为j的二叉树的个数。转移是f[i][j] = ∑(f[i - 1][k] * f[i - 1][j - k - 1])。

 

然后直接暴力是O(n3)的。在spoj上能过在bzoj上不能过。在bzoj上得用记忆化搜索来减掉一些无用的东西。虽然我觉得很烦。

 

然后试图用fft,发现fft比暴力还慢。然后试图用fnt,然后发现fnt好像不能对1e9+7这种质数用。因为1e9+6只有来一个2。真悲伤。我还是太弱啊。

 

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
 
using namespace std;
 
const int maxn = 609;
const int mod = 1000000007;
 
#define _l (long long int)
 
int f[maxn][maxn];
 
int getf(int i, int j) {
    if (i == 0)
        return j <= 1;
    else if (j == 0)
        return 1;
    else if (f[i][j] == -1) {
        f[i][j] = 0;
        for (int k = 0; k < j; ++ k)
            f[i][j] = (f[i][j] + _l getf(i - 1, k) * getf(i - 1, j - k - 1)) % mod;
    }
    return f[i][j];
}
 
int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
 
    memset(f, -1, sizeof(f));
    int t, n, k, s;
    scanf("%d", &t);
    while (t --) {
        scanf("%d%d", &n, &k);
        if (!k)
            puts((n == 1) ? "1" : "0");
        else {
            s = getf(k, n) - getf(k - 1, n);
            if (s < 0)
                s += mod;
            printf("%d\n", s);
        }
    }
}

BZOJ3105 [cqoi2013]新Nim游戏

今天wc讲的题。主要其实是用拟阵来证明贪心的正确性。就构造一下就好了。不选的集合构成一个拟阵的基,如果基不能异或出0就是线性无关的。然后按照拟阵的贪心方法挨个加。

 

策略是从大到小排个序能不选就不选,用异或消元来判断一下就好了。许久没有写了,还好没有搞忘。

 

代码还是比较短的,开心ing。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long dint;
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define _l (long long int)

const int maxn = 109;

int n, a[maxn], b[maxn], c[maxn], t;
dint s;

bool hasZero() {
	for (int i = 0; i < t; ++ i)
		b[i] = c[i];
	for (int i = 31, j = 0; i >= 0 && j < t; -- i) {
		for (int k = j; k < n; ++ k)
			if ((b[k] >> i) & 1) {
				swap(b[k], b[j]);
				break;
			}
		if ((b[j] >> i) & 1) {
			for (int k = j + 1; k < n; ++ k)
				if ((b[k] >> i) & 1) {
					b[k] ^= b[j];
					if (!b[k])
						return 1;
				}
			++ j;
		}
	}
	return 0;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif
	
	s = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; ++ i)
		scanf("%d", a + i);
	sort(a, a + n);
	t = 0;
	for (int i = n - 1; i >= 0; -- i) {
		c[t ++] = a[i];
		if (hasZero()) {
			-- t;
			s += a[i];
		}
	}
	printf(lld "\n", s);
}

BZOJ1453 [Wc]Dface双面棋盘

有离线镜像还是能省不少流量。开心。不过流量还是用得飞快啊。

 

这就是个cdq重构的题。学习了前几天xyz大爷讲的扔线段树的做法,省了不少代码。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct node {
	int v, c;
	node *next;
};
struct seg {
	int l, r;
	node *head;
	seg *ls, *rs;
};

const int maxa = 209;
const int maxn = 40009;
const int maxm = 10009;
const int maxnd = maxn * 23;
const int mov[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

#define upmax(_a_,_b_) { \
	if (_a_ < _b_) \
		_a_ = _b_; \
}
#define mkpos(_x_,_y_) (((_x_)<<16)|(_y_))
#define getx(_a_) ((_a_)>>16)
#define gety(_a_) ((_a_)&0x0000ffff)
#define nid(_x_,_y_) (((_x_)*n)+(_y_))

struct disjoint_set {
	int r[maxn], h[maxn];
	int vst[maxn], rst[maxn], ust[maxn], hst[maxn], tst;
	inline disjoint_set() {
		tst = 0;
	}
	void init(int n) {
		for (int i = 0; i < n; ++ i)
			r[i] = i, h[i] = 1;
	}
	int getRoot(int x) {
		for (; r[x] ^ x; x = r[x]);
		return x;
	}
	inline void merge(int u, int v) {
		u = getRoot(u), v = getRoot(v);
		if (h[u] < h[v])
			swap(u, v);
		vst[++ tst] = v;
		rst[tst] = r[v];
		r[v] = u;
		ust[tst] = u;
		hst[tst] = h[u];
		upmax(h[u], h[v] + 1);
	}
	void recover(int x) {
		while (tst > x) {
			r[vst[tst]] = rst[tst];
			h[vst[tst]] = hst[tst];
			-- tst;
		}
	}
};

node nlst[maxnd], *np(nlst);
seg slst[maxn * 3], *sp(slst), *rt;
int n, m, a[maxa][maxa], cc[maxa][maxa], pret[maxa][maxa], ansb[maxm], answ[maxm];

#define midp(_p_) ((_p_->l+_p_->r)>>1)

inline seg* sgtBuild(int l, int r) {
	seg* p(sp ++);
	p-> l = l;
	p-> r = r;
	p-> head = 0;
	if (l + 1 < r) {
		p-> ls = sgtBuild(l, midp(p));
		p-> rs = sgtBuild(midp(p), r);
	}
	return p;
}
inline void sgtIns(seg* p, int l, int r, int ps, int cl) {
	if (p-> l == l && p-> r == r) {
		np-> v = ps;
		np-> c = cl;
		np-> next = p-> head;
		p-> head = np ++;
	}
	else if (r <= midp(p))
		sgtIns(p-> ls, l, r, ps, cl);
	else if (l >= midp(p))
		sgtIns(p-> rs, l, r, ps, cl);
	else {
		sgtIns(p-> ls, l, midp(p), ps, cl);
		sgtIns(p-> rs, midp(p), r, ps, cl);
	}
}

disjoint_set r;

void sgtDfs(seg* p, int cb, int cw) {
	int rp(r. tst);
	for (node* it = p-> head; it; it = it-> next) {
		int x(getx(it-> v)), y(gety(it-> v)), c(it-> c), *v;
		if (c == 0)
			v = &cw;
		else
			v = &cb;
		++ *v;
		cc[x][y] = c;
		for (int mi = 0; mi < 4; ++ mi) {
			int x1(x + mov[mi][0]), y1(y + mov[mi][1]);
			if (x1 < 0 || x1 >= n || y1 < 0 || y1 >= n)
				continue;
			if (cc[x1][y1] == c && r. getRoot(nid(x, y)) != r. getRoot(nid(x1, y1))) {
				-- *v;
				r. merge(nid(x, y), nid(x1, y1));
			}
		}
	}
	if (p-> l + 1 == p-> r) {
		ansb[p-> l] = cb;
		answ[p-> l] = cw;
	}
	else {
		sgtDfs(p-> ls, cb, cw);
		sgtDfs(p-> rs, cb, cw);
	}
	for (node* it = p-> head; it; it = it-> next) {
		int x(getx(it-> v)), y(gety(it-> v));
		cc[x][y] = -1;
	}
	r. recover(rp);
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < n; ++ j) {
			scanf("%d", a[i] + j); 
			pret[i][j] = 0;
		}
	scanf("%d", &m);
	rt = sgtBuild(0, m + 1);
	for (int i = 1; i <= m; ++ i) {
		int x, y;
		scanf("%d%d", &x, &y);
		-- x;
		-- y;
		sgtIns(rt, pret[x][y], i, mkpos(x, y), a[x][y]);
		a[x][y] ^= 1;
		pret[x][y] = i;
	}
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < n; ++ j)
			sgtIns(rt, pret[i][j], m + 1, mkpos(i, j), a[i][j]);
	memset(cc, 0, sizeof(cc));
	r. init(n * n);
	memset(cc, -1, sizeof(cc));
	sgtDfs(rt, 0, 0);
	for (int i = 1; i <= m; ++ i)
		printf("%d %d\n", ansb[i], answ[i]);
}

BZOJ2642 Pku3968 Jungle Outpost

看mhy写了好几天了啊。然后研究了一下,居然过得比他早。

 

首先敌人一定可以炸连续的一段。然后就是要看炸掉任意连续一段之后还有没有剩下的公共部分,既半平面交。

 

然后我yy出了一个半平面交的做法。找出一条基准直线,按方向顺时针方向排序。然后按照正常半平面交的方法根据栈顶两个元素和当前直线的位置关系判断是否需要退栈。然后当找到一条与基准直线的角度大于等于π的直线(直线是有向的,也可以说是成负角)且它把栈里退到只剩基准直线一条的时候,这些半平面没有交。否则这些半平面有交。

 

至于证明嘛,我也不会(挠头),毕竟是感受出来的。然后感觉这次代码写得比较漂亮,开心。

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

inline double sqr(double x) {
	return x * x;
}

struct point {
	double x, y;
	inline point() {}
	inline point(double x0, double y0) {
		x = x0, y = y0;
	}
	inline void read() {
		scanf("%lf%lf", &x, &y);
	}
};
struct vect {
	double x, y;
	inline vect() {}
	inline vect(double x0, double y0) {
		x = x0, y = y0;
	}
	inline vect(point a, point b) {
		x = b. x - a. x, y = b. y - a. y;
	}
	inline vect vertical() {
		return vect(y, - x);
	}
	inline double length() {
		return sqrt(sqr(x) + sqr(y));
	}
};
struct line {
	point a;
	vect d;
	inline line() {}
	inline line(point u, point v) {
		a = u;
		d = vect(u, v);
	}
};

const double eps = 1e-8;

inline point operator +(const point& a, const vect& b) {
	return point(a. x + b. x, a. y + b. y);
}
inline double operator *(const vect& a, const vect& b) {
	return a. x * b. y - a. y * b. x;
}
inline vect operator *(const vect& a, double r) {
	return vect(a. x * r, a. y * r);
}
inline int sgn(double x) {
	return (fabs(x) < eps) ? 0 : ((x > 0) ? 1 : -1);
}

const int maxn = 50009;

int n, t, li[maxn], tst;
point a[maxn], b[maxn];
line l[maxn];

inline bool outSide(line l, point p) {
	return sgn(vect(l. a, p) * l. d) <= 0;
}

point getCross(line l1, line l2) {
	point a(l1. a), b(l1. a + l1. d);
	point c(l2. a), d(l2. a + l2. d);
	if (!sgn(vect(d, c) * vect(d, b))) {
		d = l2. a + l2. d * 2.0;
		b = l1. a + l1. d * 2.0;
	}
	if (!sgn(vect(d, a) * vect(d, c)))
		a = l1. a + l1. d * 3.0;
	double sa(vect(d, a) * vect(d, c));
	double sb(vect(d, c) * vect(d, b));
	double rat(sa / (sa + sb));
	return point(a + vect(a, b) * rat);
}

bool check(int t) {
	for (int i = 0; i < n; ++ i)
		l[i] = line(a[i], a[(i + t + 1) % n]);
	tst = 2;
	li[1] = 0;
	b[2] = getCross(l[0], l[1]);
	li[2] = 1;
	for (int i = 2; i < n; ++ i) {
		while (tst > 1 && outSide(l[i], b[tst]))
			-- tst;
		if (tst == 1 && sgn(l[i]. d * l[0]. d) <= 0)
			return 0;
		li[++ tst] = i;
		b[tst] = getCross(l[li[tst - 1]], l[i]);
	}
	return tst > 2;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	for (int i = 0; i <	n; ++ i)
		a[i]. read();
	int l(1), r(n - 2);
	while (l < r) {
		int mid((l + r) >> 1);
		if (check(mid))
			l = mid + 1;
		else
			r = mid;
	}
	printf("%d\n", l);
}

顺便把写的画图的js粘上来好了。以后说不定还有用。

JS not supported

BZOJ1815 [Shoi2006]color 有色图

一道burnside,做了几次之后感觉比较经典了。然后发现网上居然没有题解。

 

枚举正整数拆分,既每个质换的长度。然后枚举gcd算每类置换的贡献。再用错排公式来算每类置换的数量。然后求和。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define _l (long long int)

const int maxn = 63;

int n, m, mod, ans, g[maxn][maxn];
int sq[maxn], tq, fac[maxn], finv[maxn], vinv[maxn];

int modPow(int a, int x) {
	int s(1);
	for (; x; x >>= 1, a = _l a * a % mod)
		if (x & 1)
			s = _l s * a % mod;
	return s;
}

int gcd(int a, int b) {
	while (a) {
		register int c(b % a);
		b = a;
		a = c;
	}
	return b;
}

void addAns() {
	int t(0);
	for (int i = 1; i <= tq; ++ i) {
		t += sq[i] >> 1;
	//	if (sq[i] > 1)
	//		++ t;
		for (int j = 1; j < i; ++ j)
			t += g[sq[i]][sq[j]];
	}
	t = modPow(m, t);
	for (int i = 1, j; i <= tq; i = j) {
		for (j = i; j <= tq && sq[i] == sq[j]; ++ j)
			t = _l t * vinv[sq[j]] % mod;
		t = _l t * finv[j - i] % mod;
	}
	ans = (ans + t) % mod;
}

void dfs(int v, int l) {
	if (!v)
		addAns();
	else {
		++ tq;
		for (int i = 1; i <= l && i <= v; ++ i) {
			sq[tq] = i;
			dfs(v - i, i);
		}
		-- tq;
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d%d", &n, &m, &mod);
	fac[0] = finv[0] = 1;
	for (int i = 1; i <= n; ++ i) {
		fac[i] = _l fac[i - 1] * i % mod;
		finv[i] = modPow(fac[i], mod - 2);
		vinv[i] = modPow(i, mod - 2);
		for (int j = 1; j <= n; ++ j)
			g[i][j] = gcd(i, j);
	}
	tq = 0;
	dfs(n, n);
	printf("%d\n", ans);
}

BZOJ2401 陶陶的难题I

推了半天,发现还是得sqrt,过不到啊。

 

然后发现与经典的问题相比,就是n=n。然后这不是做过的那个LCM sum的前缀和就完了嘛。傻了。

 

然后第一次MLE是因为明明压了8位,还是开了30长的数组。第二次WA是因为高精输出的时候%08d写成了%8d。是不是明天又要逗极而死了。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long dint;

#define _l (long long int)

const int maxlen = 7;
const dint base = 1e8;
struct BigInt {
	dint dig[maxlen], len;
	BigInt() {
		len = 0;
	}
	void set(dint x) {
		dig[0] = x;
		len = 1;
		while (dig[len - 1] >= base) {
			dig[len] = dig[len - 1] / base;
			dig[len - 1] %= base;
			++ len;
		}
	}
	void copy(const BigInt& x) {
		len = x. len;
		for (int i = 0; i < len; ++ i)
			dig[i] = x. dig[i];
	}
	void inc(dint x) {
		if (!len) {
			dig[0] = x;
			len = 1;
		}
		else
			dig[0] += x;
		for (int i = 0; i < len - 1 && dig[i] >= base; ++ i) {
			dig[i + 1] += dig[i] / base;
			dig[i] %= base;
		}
		while (dig[len - 1] >= base) {
			dig[len] = dig[len - 1] / base;
			dig[len - 1] %= base;
			++ len;
		}
	}
	void print() {
		printf("%d", (int)dig[len - 1]);
		for (int i = len - 2; i >= 0; -- i)
			printf("%08d", (int)dig[i]);
		putchar(10);
	}
};

const int maxn = 1000009;

int tp, pn[maxn], phi[maxn];
dint tot[maxn];
BigInt ans[maxn];
bool pr[maxn];

void pre(int n) {
	memset(pr, 0, sizeof(pr));
	tp = 0;
	phi[1] = 1;
	for (int i = 2; i <= n; ++ i) {
		if (!pr[i]) {
			pn[tp ++] = i;
			phi[i] = i - 1;
		}
		for (int j = 0, t; j < tp && (t = i * pn[j]) <= n; ++ j) {
			pr[t] = 1;
			if (i % pn[j])
				phi[t] = phi[i] * phi[pn[j]];
			else {
				phi[t] = phi[i] * pn[j];
				break;
			}
		}
	}
	for (int i = 1; i <= n; ++ i) {
		dint d = (i == 1) ? 1 : (_l i * phi[i] >> 1);
		for (int j = 1, t; (t = j * i) <= n; ++ j)
			tot[t] += d;
	}
	ans[1]. set(1);
	for (int i = 2; i <= n; ++ i) {
		tot[i] = tot[i] * i;
		ans[i]. copy(ans[i - 1]);
		ans[i]. inc(tot[i] * 2 - i);
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	int t;
	scanf("%d", &t);
	pre(1e6);
	while (t --) {
		int n;
		scanf("%d", &n);
		ans[n]. print();
	}
}

BZOJ3435 [Wc2014]紫荆花之恋

想起去年在考场上完全没有思路不想写数据结构于是骗了30分。现在想想当初还是naive。

 

这题看上去就是点分治。因为要动态加点所以要动态。然后利用替罪羊树的方法重构树上的一团东西(不能叫团,否则命名重复了)。

 

本来以为要写很久,结果也没写多久。奇怪的是,把maintain展开到insert里面之后就跑得飞快了,否则过不到而且根本跑不下来9和10。又丑了郁闷。

 

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>

using namespace std;

const int buf_len = 4000;
const size_t buf_size = buf_len * sizeof(char);
char buf[buf_len], *bufb(buf), *bufe(buf + 1);
#define readBuf() { \
	if (++ bufb == bufe) \
		bufe = (bufb = buf) + fread(buf, 1, buf_size, stdin); \
}
#define readInt(_x_) { \
	register int _s_(0); \
	do { \
		readBuf(); \
	} while (!isdigit(*bufb)); \
	do { \
		_s_ = _s_ * 10 + *bufb - 48; \
		readBuf(); \
	} while (isdigit(*bufb)); \
	_x_ = _s_; \
}

struct edge {
	int t, v;
	edge *next;
};
struct sbt_node {
	int vl, sz;
	sbt_node *ls, *rs;
};

typedef long long dint;

#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define _l (long long int)

const int maxn = 100009;
const int fmod = 1e9;
const int maxl = 19;
const int maxnd = maxn * maxl * 2;
const int balc = 3;
const double ublim = 0.9;
const int inf = 0x3f3f3f3f;

dint lans;
int n;
int ath[maxn][maxl], drt[maxn], dep[maxn];
int bd[maxn], bsz[maxn], bfa[maxn], br[maxn], r[maxn];
int stsz[maxn], vis[maxn], vist;
sbt_node slst[maxnd], *sstc[maxnd], **sp, *brt[maxn], *prt[maxn];
edge *head[maxn], elst[maxn << 1], *ep(elst);

void pre() {
	for (int i = 0; i < maxnd; ++ i) {
		sstc[i] = slst + i;
		slst[i]. ls = slst[i]. rs = 0;
	}
	sp = sstc;
	vist = 0;
	memset(vis, 0, sizeof(vis));
}

inline void addEdge(int u, int v, int w) {
	ep-> t = v;
	ep-> v = w;
	ep-> next = head[u];
	head[u] = ep ++;
}

#define szof(_p_) ((_p_)?(_p_->sz):0)
inline sbt_node* allocNode() {
	static sbt_node* ret;
	ret = *(sp ++);
	if (ret-> ls)
		*(-- sp) = ret-> ls;
	if (ret-> rs)
		*(-- sp) = ret-> rs;
	return ret;
}
inline void lRot(sbt_node* &p) {
	register sbt_node* q(p-> ls);
	p-> ls = q-> rs;
	q-> rs = p;
	q-> sz = p-> sz;
	p-> sz = szof(p-> ls) + szof(p-> rs) + 1;
	p = q;
}
inline void rRot(sbt_node* &p) {
	register sbt_node* q(p-> rs);
	p-> rs = q-> ls;
	q-> ls = p;
	q-> sz = p-> sz;
	p-> sz = szof(p-> ls) + szof(p-> rs) + 1;
	p = q;
}
inline void sbtIns(sbt_node* &p, int v0) {
	if (!p) {
		p = allocNode();
		p-> ls = p-> rs = 0;
		p-> sz = 1;
		p-> vl = v0;
	}
	else {
		++ p-> sz;
		if (v0 < p-> vl) {
			sbtIns(p-> ls, v0);
			if (szof(p-> ls) > szof(p-> rs) + balc)
				lRot(p);
		}
		else {
			sbtIns(p-> rs, v0);
			if (szof(p-> ls) + balc < szof(p-> rs))
				rRot(p);
		}
	}
}
int sbtCntLower(sbt_node* p, int v0) {
	int s(0);
	while (p)
		if (v0 < p-> vl)
			p = p-> ls;
		else
			s += szof(p-> ls) + 1, p = p-> rs;
	return s;
}

int LCA(int u, int v) {
	if (dep[u] < dep[v])
		swap(u, v);
	for (int i = maxl - 1; i >= 0; -- i)
		if (dep[ath[u][i]] >= dep[v])
			u = ath[u][i];
	if (u == v)
		return u;
	for (int i = maxl - 1; i >= 0; -- i)
		if (ath[u][i] ^ ath[v][i]) {
			u = ath[u][i];
			v = ath[v][i];
		}
	return ath[u][0];
}
inline int dist(int u, int v) {
	int a(LCA(u, v));
	return drt[u] + drt[v] - drt[a] * 2;
}

void insEach(sbt_node* q, sbt_node* &x, int mn) {
	static sbt_node* stc[maxn];
	int tst(1);
	stc[1] = q;
	while (tst) {
		sbt_node* p(stc[tst --]);
		sbtIns(x, p-> vl + mn);
				if (p-> ls)
			stc[++ tst] = p-> ls;
		if (p-> rs)
			stc[++ tst] = p-> rs;
	}
}
int build(int pbd, int pbr, int pbf) {
	static int q[maxn], fr[maxn], dst[maxn];
	int hd(0), tl(1), ct(0), cs(inf);
	q[0] = pbr;
	fr[pbr] = 0;
	dst[pbr] = 0;
	while (hd < tl) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next)
			if (bd[e-> t] == inf && e-> t != fr[p]) {
				fr[e-> t] = p;
				dst[e-> t] = dst[p] + e-> v;
				q[tl ++] = e-> t;
			}
	}
	for (int ti = tl - 1, p; ti >= 0; -- ti) {
		int ms(1);
		p = q[ti];
		stsz[p] = 1;
		for (edge* e = head[p]; e; e = e-> next)
			if (bd[e-> t] == inf && e-> t != fr[p]) {
				stsz[p] += stsz[e-> t];
				if (ms < stsz[e-> t])
					ms = stsz[e-> t];
			}
		if (ms < tl - stsz[p])
			ms = tl - stsz[p];
		if (ms < cs) {
			cs = ms;
			ct = p;
		}
	}
	for (int ti = 0, p; ti < tl; ++ ti) {
		p = q[ti];
		sbtIns(prt[ct], dst[p] - r[p]);
			}
	bd[ct] = pbd;
	bsz[ct] = tl;
	bfa[ct] = pbf;
	br[ct] = pbr;
	sbtIns(brt[ct], -r[ct]);
		for (edge* e = head[ct]; e; e = e-> next)
		if (bd[e-> t] == inf) {
			int nr(build(pbd + 1, e-> t, ct));
			insEach(prt[nr], brt[ct], e-> v);
		}
	return ct;
}

void rebuild(int p0, int pbd, int pbr, int pbf) {
	static int q[maxn];
	int hd(0), tl(1);
	++ vist;
	vis[p0] = vist;
	q[0] = p0;
	while (hd < tl) {
		int p(q[hd ++]);
		*(-- sp) = brt[p];
		*(-- sp) = prt[p];
		brt[p] = 0;
		prt[p] = 0;
		for (edge* e = head[p]; e; e = e-> next)
			if (bd[e-> t] > pbd && vis[e-> t] < vist) {
				vis[e-> t] = vist;
				q[tl ++] = e-> t;
			}
	}
	for (int i = 0; i < tl; ++ i)
		bd[q[i]] = inf;
	build(pbd, pbr, pbf);
}

void addNode(int p) {
	brt[p] = 0;
	sbtIns(brt[p], -r[p]);
		prt[p] = 0;
	sbtIns(prt[p], -r[p]);
		if (p == 1) {
		bd[p] = 1;
		bsz[p] = 1;
		bfa[p] = 0;
		br[p] = 1;
	}
	else {
		register int fa(ath[p][0]);
		bd[p] = bd[fa] + 1;
		bsz[p] = 1;
		bfa[p] = fa;
		br[p] = p;
		int q(fa), l(p), ubp, dt(0);
		double ubv(0);
		while (q) {
			++ bsz[q];
			if ((double)bsz[l] / bsz[q] > ubv) {
				ubv = (double)bsz[l] / bsz[q];
				ubp = q;
			}
			int d0(dist(p, q));
			sbtIns(brt[q], d0 - r[p]);
			sbtIns(prt[q], dist(br[q], p) - r[p]);
			dt += sbtCntLower(brt[q], r[p] - d0) - sbtCntLower(prt[l], r[p] - d0 - dist(br[l], q));
			l = q;
			q = bfa[q];
		}
		lans += dt;
		if (ubv > ublim)
			rebuild(ubp, bd[ubp], br[ubp], bfa[ubp]);
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	pre();
	readInt(n);
	readInt(n);
	lans = 0;
	for (int i = 1; i <= n; ++ i) {
		int fa;
		readInt(fa);
		readInt(drt[i]);
		readInt(r[i]);
		if (i > 1) {
			fa ^= lans % fmod;
			dep[i] = dep[fa] + 1;
			addEdge(fa, i, drt[i]);
			addEdge(i, fa, drt[i]);
			drt[i] += drt[fa];
			ath[i][0] = fa;
			for (int j = 1; j < maxl; ++ j)
				ath[i][j] = ath[ath[i][j - 1]][j - 1];
		}
		else {
			dep[i] = 1;
		}
		addNode(i);
		printf(lld "\n", lans);
	}
}

BZOJ2653 middle

老久之前就看到过的题,不过不会做。今天晚上也是花了差不多俩小时才调过,虽然大概一个半小时都在纠结二分怎么写的问题。我没救了。

 

考虑经典的中位数二分,是把≤x的数字取-1,>x的数取1,然后算最大子串看是否大于-1或1中的某一个,具体要看题目要求偶数是取上整还是下整什么的。

 

然后这题就把所有东西排序之后拿可持久化线段树把这个序列搞出来,然后照着之前的思路二分就好了。线段树用来维护最大和最小前缀和。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct dat {
	int sm, s;
};
struct seg {
	int l, r;
	dat v[2];
	seg *ls, *rs;
};

#define _l (long long int)

typedef pair <int, int> vpr;
typedef dat(*dat_mf)(const dat&, const dat&);

const int maxn = 20009;
const int maxnd = maxn * 19;
const dat null_str = {0, 0};
const dat neg_str0 = {-1, -1};
const dat neg_str1 = {-1, -1};
const dat pos_str0 = {1, 1};
const dat pos_str1 = {1, 1};

vpr a[maxn];
int n, m, q[4], lans, maxv;
seg slst[maxnd], *sp(slst), *rt[maxn];

#define mid(p) ((p->l+p->r)>>1)

dat ret;
inline dat merd0(const dat& a, const dat& b) {
	ret. sm = a. sm + b. sm;
	ret. s = min(a. s, a. sm + b. s);
	return ret;
}
inline dat merd1(const dat& a, const dat& b) {
	ret. sm = a. sm + b. sm;
	ret. s = max(a. s, a. sm + b. s);
	return ret;
}
const dat_mf dmf[2] = {merd0, merd1};
inline dat addSum(const int& v, const dat& a) {
	ret. s = a. s + v;
	ret. sm = a. sm + v;
	return ret;
}

seg* sgtBuild(int l, int r) {
	seg *p(sp ++);
	p-> l = l;
	p-> r = r;
	if (l + 1 == r) {
		p-> v[0] = neg_str0;
		p-> v[1] = neg_str1;
	}
	else {
		p-> ls = sgtBuild(l, mid(p));
		p-> rs = sgtBuild(mid(p), r);
		p-> v[0] = merd0(p-> ls-> v[0], p-> rs-> v[0]);
		p-> v[1] = merd1(p-> ls-> v[1], p-> rs-> v[1]);
	}
	return p;
}

seg* sgtChg(seg* q, int p0) {
	seg* p(sp ++);
	p-> l = q-> l;
	p-> r = q-> r;
	if (p-> l + 1 == p-> r) {
		p-> v[0] = pos_str0;
		p-> v[1] = pos_str1;
	}
	else {
		if (p0 < mid(p)) {
			p-> ls = sgtChg(q-> ls, p0);
			p-> rs = q-> rs;
		}
		else {
			p-> ls = q-> ls;
			p-> rs = sgtChg(q-> rs, p0);
		}
		p-> v[0] = merd0(p-> ls-> v[0], p-> rs-> v[0]);
		p-> v[1] = merd1(p-> ls-> v[1], p-> rs-> v[1]);
	}
	return p;
}

int qv;
dat sgtQry(seg* p, int l, int r) {
	if (p-> l == l && p-> r == r)
		return p-> v[qv];
	else if (r <= mid(p))
		return sgtQry(p-> ls, l, r);
	else if (l >= mid(p))
		return addSum(p-> ls-> v[0]. sm, sgtQry(p-> rs, l, r));
	else
		return dmf[qv](sgtQry(p-> ls, l, mid(p)), sgtQry(p-> rs, mid(p), r));
}

void pstat(seg* p, bool sr = 1) {
	if (p-> l + 1 == p-> r)
		printf("%d ", p-> v[0]. sm);
	else {
		pstat(p-> ls, 0);
		pstat(p-> rs, 0);
	}
	if (sr)
		putchar(10);
}

int minSum(int x) {
	int v0, v1;
	qv = 1;
	if (q[0] == 0)
		v0 = max(0, sgtQry(rt[x], 0, q[1]). s);
	else
		v0 = sgtQry(rt[x], q[0] - 1, q[1]). s;
	qv = 0;
	v1 = sgtQry(rt[x], q[2], q[3] + 1). s;
	return v1 - v0;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	for (int i = 0; i < n; ++ i)
		scanf("%d", &a[i]. first), a[i]. second = i;
	sort(a, a + n);
	rt[0] = sgtBuild(0, n);
	for (int i = 1; i <= n; ++ i)
		rt[i] = sgtChg(rt[i - 1], a[i - 1]. second);
	scanf("%d", &m);
	lans = 0;
	while (m --) {
		for (int i = 0; i < 4; ++ i) {
			scanf("%d", q + i); 
			q[i] = (_l q[i] + lans) % n;
		}
		sort(q, q + 4);
		int l(0), r(n - 1);
		while (l < r) {
			int md((l + r + 1) >> 1);
			if (minSum(md) > 0)
				r = md - 1;
			else
				l = md;
		}
		printf("%d\n", (lans = a[l]. first));
	}
}

BZOJ3351 [ioi2009]Regions

ioi的数据结构题,果然还是比较BT的。

 

做法是对颜色分开,大的颜色预处理,小的颜色直接暴力。

 

然后一个小错又坑了好久。我真的没救了。

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <algorithm>

using namespace std;

const int buf_len = 4000;
const size_t buf_size = buf_len * sizeof(char);
char buf[buf_len], *bufb(buf), *bufe(buf + 1);
#define readBuf() { \
	if (++ bufb == bufe) \
		bufe = (bufb = buf) + fread(buf, 1, buf_size, stdin); \
}
#define readInt(_x_) { \
	register int _s_(0); \
	do { \
		readBuf(); \
	} while (!isdigit(*bufb)); \
	do { \
		_s_ = _s_ * 10 + *bufb - 48; \
		readBuf(); \
	} while (isdigit(*bufb)); \
	_x_ = _s_; \
}

struct edge {
	int t;
	edge *next;
};
typedef pair <int, int> ninfo;
typedef long long dint;

#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif

const int maxn = 200009;
const int maxb = 259;
const int maxbn = 800;
const int maxc = 25009;

int n, r, q, c[maxn], bi[maxc], bc[maxn], nod[maxn];
int dfb[maxn], dfe[maxn], dfo[maxn];
dint bn[maxb][maxc], nb[maxb][maxc];
edge *head[maxn], elst[maxn], *ep(elst);
ninfo ni[maxn], ci[maxn];

struct bin_tree {
	int a[maxn], t[maxn], c;
	bin_tree() {
		c = 0;
		memset(t, 0, sizeof(t));
	}
	void chg(int p, int v) {
		for (; p <= n; p += (p & -p))
			if (t[p] < c)
				t[p] = c, a[p] = v;
			else
				a[p] += v;
	}
	int qry(int p) {
		int s(0);
		for (; p; p -= (p & -p))
			if (t[p] == c)
				s += a[p];
		return s;
	}
};

bin_tree bt;

inline void addEdge(int u, int v) {
	ep-> t = v;
	ep-> next = head[u];
	head[u] = ep ++;
}

void buildTree() {
	static int stc[maxn];
	int tst(1), dfn(0);
	stc[1] = 1;
	while (tst) {
		int p(stc[tst --]);
		dfo[dfb[p] = dfe[p] = ++ dfn] = p;
		for (edge* e = head[p]; e; e = e-> next)
			stc[++ tst] = e-> t;
	}
	for (int ti = n; ti; -- ti) {
		int p(dfo[ti]);
		for (edge* e = head[p]; e; e = e-> next)
			if (dfe[e-> t] > dfe[p])
				dfe[p] = dfe[e-> t];
	}
}

void preAns() {	
	static int ca[maxn], cb[maxn];
	memset(bn, 0, sizeof(bn));
	memset(nb, 0, sizeof(nb));
	for (int i = 1; i <= n; ++ i)
		ni[i - 1] = ninfo(c[i], i);
	sort(ni, ni + n);
	memset(cb, 0, sizeof(cb));
	memset(bi, -1, sizeof(bi));
	int tb(0);
	for (int i = 0, j; i < n; i = j) {
		for (j = i; j < n && ni[j]. first == ni[i]. first; ++ j)
			nod[j] = ni[j]. second;
		bc[ni[i]. first] = i;
		ci[tb ++] = ninfo(i - j, ni[i] .first);
	}
	sort(ci, ci + tb);
	for (int ti = 0; ti < tb && ti < maxb; ++ ti) {
		int i(bc[ci[ti]. second]), j(i - ci[ti]. first);
		bi[ni[i]. first] = ti;
		memset(ca, 0, sizeof(ca));
		memset(cb, 0, sizeof(cb));
		for (int k = i; k < j; ++ k) {
			++ ca[dfb[nod[k]]];
			++ cb[dfb[nod[k]]];
			-- cb[dfe[nod[k]] + 1];
		}
		for (int k = 1; k <= n; ++ k)
			ca[k] += ca[k - 1], cb[k] += cb[k - 1];
		for (int p = 1; p <= n; ++ p) {
			nb[ti][c[p]] += ca[dfe[p]] - ca[dfb[p] - 1];
			bn[ti][c[p]] += cb[dfb[p]];
		}
	}
}

dint query(int r1, int r2) {
	dint ans(0);
	++ bt. c;
	for (int i = bc[r2]; i < n && c[nod[i]] == r2; ++ i)
		bt. chg(dfb[nod[i]], 1);
	for (int i = bc[r1]; i < n && c[nod[i]] == r1; ++ i)
		ans += bt. qry(dfe[nod[i]]) - bt. qry(dfb[nod[i]] - 1);
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	//freopen("in.txt", "r", stdin);
#endif

	readInt(n);
	readInt(r);
	readInt(q);
	readInt(c[1]);
	memset(head, 0, sizeof(head));
	for (int i = 2; i <= n; ++ i) {
		int u;
		readInt(u);
		readInt(c[i]);
		addEdge(u, i);
	}
	buildTree();
	preAns();
	while (q --) {
		int r1, r2;
		readInt(r1);
		readInt(r2);
		if (bi[r1] > -1)
			printf(lld "\n", bn[bi[r1]][r2]);
		else if (bi[r2] > -1)
			printf(lld "\n", nb[bi[r2]][r1]);
		else
			printf(lld "\n", query(r1, r2));
	}
}

BZOJ1502 [NOI2005]月下柠檬树

今天几乎讲了一天的算几。或者说能听的只有算几。然后就去做了一道基础题。

 

刚开始的时候比较naive,把相交的情况想简单了,打算直接上数学,然后悲剧地发现还有其它的不好算交点的情况,于是就去写simpson了。

 

simpson就是一个估计用的,s = (4 * f(mid) + f(l) + f(r))*(r-l)/6。至于为啥是对的就不知道了。反正比较能用。然后每次直接去扫一遍所有能交的地方,取max就行了,比挨个算交点方便许多。

 

然后第一次写,代码也比较乱。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

const int maxn = 509;
const double pi = asin(1) * 2;
const double eps = 1e-6;

#define x1 _x1_
#define x2 _x2_
#define y1 _y1_
#define y2 _y2_

int n;
double tht, x[maxn], h[maxn], r[maxn];
double crd[maxn];
double x1[maxn], x2[maxn], y1[maxn], y2[maxn];
bool c[maxn];

inline int sgn(double x) {
	if (fabs(x) < eps)
		return 0;
	else
		return (x < 0) ? -1 : 1;
}

inline double sqr(double x) {
	return x * x;
}

void calc() {
	for (int i = 0; i < n; ++ i) {
		int u(i), v(i + 1);
		if (sgn(x[u] - r[u] - x[v] + r[v]) <= 0 && sgn(x[u] + r[u] - x[v] - r[v]) >= 0) {
			c[i] = 0;
		}
		else {
			double ctht((r[u] - r[v]) / (x[v] - x[u])), stht(sqrt(1 - sqr(ctht)));
			x1[i] = x[u] + r[u] * ctht; 
			y1[i] = r[u] * stht;
			x2[i] = x[v] + r[v] * ctht;
			y2[i] = r[v] * stht;
			c[i] = 1;
		}
	}
}

double f(double x0) {
	double y(0);
	for (int i = 0; i <= n; ++ i)
		if (x0 >= x[i] - r[i] && x0 <= x[i] + r[i])
			y = max(y, sqrt(sqr(r[i]) -  sqr(x0 - x[i])));
	for (int i = 0; i < n; ++ i)
		if (c[i] && sgn(x0 - x1[i]) >= 0 && sgn(x0 - x2[i]) <= 0)
			y = max(y, (y1[i] * (x2[i] - x0) + y2[i] * (x0 - x1[i])) / (x2[i] - x1[i]));
	return y;
}

inline double integ(double l, double r) {
	return (f(l) + f(r) + f((l + r) / 2) * 4) * (r - l) / 6;
}

double simpson(double l0, double r0) {
	double mid((l0 + r0) / 2.0);
	double a(integ(l0, r0));
	double b(integ(l0, mid));
	double c(integ(mid, r0));
	if (fabs(a - b - c) < eps)
		return b + c;
	else
		return simpson(l0, mid) + simpson(mid, r0);
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	double l0, r0;
	scanf("%d%lf", &n, &tht);
	for (int i = 0; i <= n; ++ i)
		scanf("%lf", h + i);
	for (int i = 0; i <= n; ++ i) {
		if (i)
			h[i] += h[i - 1];
		x[i] = h[i] / tan(tht);
	}
	l0 = r0 = x[n];
	for (int i = 0; i < n; ++ i) {
		scanf("%lf", r + i);
		l0 = min(l0, x[i] - r[i]);
		r0 = max(r0, x[i] + r[i]);
	}
	r[n] = 0;
	calc();
	printf("%.2lf\n", simpson(l0, r0) * 2.0);
}

BZOJ2816 [ZJOI2012]网络

听说是水水的splay,果然是。

 

乍一看要用lct,然后因为度数只有2,所以肯定是一条链。然后就直接用splay来维护。然后因为少写了句splay(v)导致多调了至少一节课的时间。我没救了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>

using namespace std;

#define upmax(_a_,_b_) { \
	if (_a_ < _b_) \
		_a_ = _b_; \
}

struct splay_node {
	int vl, vm, rv;
	splay_node *pt, *ls, *rs;
	inline void update() {
		vm = vl;
		if (ls)
			upmax(vm, ls-> vm);
		if (rs)
			upmax(vm, rs-> vm);
	}
	inline void fix() {
		if (rv) {
			swap(ls, rs);
			if (ls)
				ls-> rv ^= 1;
			if (rs)
				rs-> rv ^= 1;
			rv = 0;
		}
	}
};

typedef pair <int, int> edge;
typedef pair <edge, int> mpr;
typedef map <edge, int> emap;

const int maxn = 200009;
const int maxc = 13;

#define nid(_p_,_c_) ((_p_)+(_c_)*n)
#define eid(_e_) ((_e_)+eb)

splay_node slst[maxn], *sr[maxn];
int v[maxn], n, m, c, q, eb, nc[maxn][maxc], ce[maxn];
emap el;

inline void rot(splay_node* q) {
	splay_node *p(q-> pt), *a(p-> pt);
	if (a) {
		if (p == a-> ls) 
			a-> ls = q; 
		else 
			a-> rs = q; 
	}
	p-> pt = q;
	q-> pt = a;
	if (q == p-> ls) {
		if (q-> rs)
			q-> rs-> pt = p;
		p-> ls = q-> rs;
		q-> rs = p;
	}
	else {
		if (q-> ls)
			q-> ls-> pt = p;
		p-> rs = q-> ls;
		q-> ls = p;
	}
	p-> update();
	q-> update();
}

void splay(splay_node* q) {
	static splay_node* stc[maxn];
	int tst(1);
	stc[tst] = q;
	for (splay_node* p = q; p-> pt; p = p-> pt)
		stc[++ tst] = p-> pt;
	for (; tst; -- tst)
		stc[tst]-> fix();
	while (q-> pt) {
		splay_node *p(q-> pt);
		if (!p-> pt || !p-> pt-> pt)
			rot(q);
		else {
			if ((q == p-> ls) == (p == p-> pt-> ls)) {
				rot(p);
				rot(q);
			}
			else {
				rot(q);
				rot(q);
			}
		}
	}
}

splay_node* expose(splay_node* p) {
	splay(p);
	while (p) {
		p-> fix();
		if (p-> ls)
			p = p-> ls;
		else
			break;
	}
	return p;
}

void link(int e0, int u0, int v0) {
	splay_node *e(sr[e0]), *u(sr[u0]), *v(sr[v0]);
	splay(u);
	if (u-> fix(), u-> rs)
		u-> rv ^= 1;
	splay(v);
	if (v-> fix(), v-> ls)
		v-> rv ^= 1;
	e-> rv = 0;
	e-> ls = u;
	u-> pt = e;
	e-> rs = v;
	v-> pt = e;
	e-> update();
}

void cut(int e0) {
	splay_node* e(sr[e0]);
	splay(e);
	e-> fix();
	if (e-> ls) {
		e-> ls-> pt = 0;
		e-> ls = 0;
	}
	if (e-> rs) {
		e-> rs-> pt = 0;
		e-> rs = 0;
	}
	e-> update();
}

int query(int u0, int v0) {
	splay_node *u(sr[u0]), *v(sr[v0]);
	if (expose(u) != expose(v))
		return -1;
	if (u == v)
		return u-> vl;
	int ans(max(u-> vl, v-> vl));
	splay(u);
	splay(v);
	if (u == v-> ls) {
		if (u-> rs)
			upmax(ans, u-> rs-> vm);
	}
	else if (u == v-> rs) {
		if (u-> ls)
			upmax(ans, u-> ls-> vm);
	}
	return ans;
}

int main() {
	scanf("%d%d%d%d", &n, &m, &c, &q);
	eb = n * c;
	for (int i = 1; i <= eb + m; ++ i)
		sr[i] = slst + i;
	for (int i = 1; i <= n; ++ i) {
		scanf("%d", v + i);
		for (int j = 0; j < c; ++ j) {
			splay_node* p(sr[nid(i, j)]);
			p-> vl = p-> vm = v[i];
		}
	}
	for (int i = 1; i <= m; ++ i) {
		splay_node* p(sr[eid(i)]);
		p-> vl = p-> vm = 0;
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		link(eid(i), nid(u, w), nid(v, w));
		ce[i] = w;
		++ nc[u][w];
		++ nc[v][w];
		if (u > v)
			swap(u, v);
		el. insert(mpr(edge(u, v), i));
	}
	while (q --) {
		int opt, u, v, w;
		scanf("%d", &opt);
		if (opt == 0) {
			scanf("%d%d", &u, &v);
			for (int i = 0; i < c; ++ i) {
				splay_node* p(sr[nid(u, i)]);
				p-> vl = v;
				p-> update();
				splay(p);
			}
		}
		else if (opt == 1) {
			scanf("%d%d%d", &u, &v, &w);
			if (u > v)
				swap(u, v);
			emap :: iterator it = el. find(edge(u, v));
			if (it == el. end())
				puts("No such edge.");
			else if (w == ce[it-> second])
				puts("Success.");
			else if (nc[u][w] > 1 || nc[v][w] > 1)
				puts("Error 1.");
			else if (expose(sr[nid(u, w)]) == expose(sr[nid(v, w)]))
				puts("Error 2.");
			else {
				int e(it-> second);
				-- nc[u][ce[e]];
				-- nc[v][ce[e]];
				cut(eid(e));
				ce[e] = w;
				++ nc[u][ce[e]];
				++ nc[v][ce[e]];
				link(eid(e), nid(u, w), nid(v, w));
				puts("Success.");
			}
		}
		else {
			scanf("%d%d%d", &w, &u, &v);
			printf("%d\n", query(nid(u, w), nid(v, w)));
		}
	}
}

BZOJ3870 Our happy ending

立杰出的题,orz。

 

一个时间比较微妙的状压DP。用一个二进制状态表示0~k中哪些数已经可以被表示出来了,然后大于k的数直接乘,因为对状态不产生影响。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define pow2(x) (1<<(x))
#define _l (long long int)

const int maxn = 21;
const int maxb = pow2(maxn) + 9;
const int mod = 1e9 + 7;

int n, k, l, f[2][maxb], zu, va;

#define incm(_a_,_b_) { \
	_a_ +=  _b_; \
	if (_a_ >= mod) \
		_a_ %= mod; \
}

int sov() {
	int prv(0), cur(1);
	memset(f, 0, sizeof(f));
	va = max(0, l - k) + 1;
	zu = pow2(k + 1) - 1;
	f[cur][1] = 1;
	for (int ti = 0; ti < n; ++ ti) {
		swap(cur, prv);
		for (int i = zu; i; -- i)
			f[cur][i] = 0;
		for (int i = zu; i; -- i)
			if (f[prv][i]) {
				for (int j = 1; j <= k; ++ j) {
					int zn((i | (i << j)) & zu);
					incm(f[cur][zn], f[prv][i]);
				}
				incm(f[cur][i], _l f[prv][i] * va % mod);
			}
	}
	int ans(0);
	for (int i = zu, e = pow2(k); i >= e; -- i)
		incm(ans, f[cur][i]);
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	int t;
	scanf("%d", &t);
	while (t --) {
		scanf("%d%d%d", &n, &k, &l);
		printf("%d\n", sov());
	}
}

BZOJ2815 [ZJOI2012]灾难

灭绝树的模板题么。之前不知道这题,差点当原创题出出去了。

 

好像有不少人不知道这东西的样子?orz zhx。

 

灭绝树只有一句口诀:一个东西的父亲是所有入边的另一端的点的LCA。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct edge {
	int t;
	edge *next;
};

const int maxn = 70009;
const int maxl = 19;
const int maxe = 2000009;

int n, ind[maxn], oud[maxn], tpo[maxn], ath[maxn][maxl], sz[maxn], d[maxn];
edge *ha[maxn], *hr[maxn], *ht[maxn];

inline void addEdge(edge** head, int u, int v) {
	edge* ep = new edge;
	ep-> t = v;
	ep-> next = head[u];
	head[u] = ep;
}

int LCA(int u, int v) {
	if (d[u] < d[v])
		swap(u, v);
	for (int i = maxl - 1; i >= 0; -- i)
		if (d[ath[u][i]] >= d[v])
			u = ath[u][i];
	if (u == v)
		return u;
	for (int i = maxl - 1; i >= 0; -- i)
		if (ath[u][i] ^ ath[v][i]) {
			u = ath[u][i];
			v = ath[v][i];
		}
	return ath[u][0];
}

void topSort() {
	static int q[maxn];
	int hd(0), tl(0);
	for (int i = 1; i <= n; ++ i)
		if (!ind[i])
			tpo[tl ++] = i;
	while (hd < tl) {
		int p(tpo[hd ++]);
		for (edge* e = ha[p]; e; e = e-> next) {
			-- ind[e-> t];
			if (!ind[e-> t])
				tpo[tl ++] = e-> t;
		}
	}
	d[0] = 0;
	for (int ti = 0; ti < n; ++ ti) {
		int p(tpo[ti]), a;
		if (!hr[p]) {
			a = 0;
		}
		else {
			a = hr[p]-> t;
			for (edge* e = hr[p]-> next; e; e = e-> next)
				a = LCA(a, e-> t);
		}
		addEdge(ht, a, p);
		ath[p][0] = a;
		for (int i = 1; i < maxl; ++ i)
			ath[p][i] = ath[ath[p][i - 1]][i - 1];
		d[p] = d[a] + 1;
	}
	for (int ti = n - 1; ti >= 0; -- ti) {
		int p(tpo[ti]);
		sz[p] = 1;
		for (edge* e = ht[p]; e; e = e-> next)
			sz[p] += sz[e-> t];
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i) {
		int v;
		while (scanf("%d", &v), v) {
			addEdge(ha, v, i);
			addEdge(hr, i, v);
			++ oud[v];
			++ ind[i];
		}
	}
	topSort();
	for (int i = 1; i <= n; ++ i)
		printf("%d\n", sz[i] - 1);
}

BZOJ3251 树上三角形

原来在win下也有敲回车的bug。开心。

 

这题坑啊。暴力还是很好想的,如果个数超过几十个的话就肯定有解了。比较坑的是两边之和大于第三边,边权231-1然后加起来就超过int的范围了。连WA若干次再见。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct edge {
	int t;
	edge *next;
};

const int maxn = 100009;
const int maxc = 88;

int n, m, v[maxn], d[maxn], fa[maxn], c[maxc];
edge *head[maxn], elst[maxn << 1], *ep(elst);

void bfs() {
	static int q[maxn];
	int hd(0), tl(1);
	q[0] = 1;
	memset(d, 0, sizeof(d));
	d[1] = 1;
	while (hd < tl) {
		int p(q[hd ++]);
		for (edge* e = head[p]; e; e = e-> next) 
			if (!d[e-> t]) {
				d[e-> t] = d[p] + 1;
				fa[e-> t] = p;
				q[tl ++] = e-> t;
			}
	}
}

bool qry(int x, int y) {
	int t(0);
	while (t < 60 && x != y) {
		if (d[x] > d[y]) {
			c[t ++] = v[x];
			x = fa[x];
		}
		else {
			c[t ++] = v[y];
			y = fa[y];
		}
	}
	if (x == y)
		c[t ++] = v[x];
	else
		return 1;
	sort(c, c + t);
	for (int i = 2; i < t; ++ i)
		if (c[i] - c[i - 1] < c[i - 2])
			return 1;
	return 0;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++ i)
		scanf("%d", v + i);
	for (int i = 1; i < n; ++ i) {
		int u, v;
		scanf("%d%d", &u, &v);
		ep-> t = v;
		ep-> next = head[u];
		head[u] = ep ++;
		ep-> t = u;
		ep-> next = head[v];
		head[v] = ep ++;
	}
	bfs();
	while (m --) {
		int t, x, y;
		scanf("%d%d%d", &t, &x, &y);
		if (t == 0)
			puts(qry(x, y) ? "Y" : "N");
		else 
			v[x] = y;
	}
}

BZOJ2419 电阻

记得这是高一的时候ty同学和我说的东西了。然后现在才解决,可以打败上帝造题的七分钟,荣登解决时间最长的题目了。

 

其实也就是一个高斯消元,用电势来列方程。不过高斯消元写得不熟,平时有空都写ds的题去了。以后不能再这样了。

 

用自己的代码高亮脚本了,虽然还不完善不过还是很开心的。

 

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>

using namespace std;

const int maxn = 109;
const double inf = 1e100;
const double eps = 1e-8;

double x[maxn], a[maxn][maxn], b[maxn][maxn];
int n, m, perm[maxn];

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	srand(19970911);
	while (scanf("%d%d", &n, &m) != EOF) {
		for (int i = 1; i <= n; ++ i)
			perm[i] = i;
		if (n > 2)
			random_shuffle(perm + 2, perm + n);
		for (int i = 1; i <= n; ++ i)
			for (int j = 1; j <= n; ++ j)
				if (i == j)
					a[i][j] = 0;
				else
					a[i][j] = inf;
		while (m --) {
			int u, v;
			double p;
			scanf("%d%d%lf", &u, &v, &p);
			u = perm[u];
			v = perm[v];
			if (u == v)
				continue;
			a[u][v] = 1.0 / (1.0 / a[u][v] + 1.0 / p);
			a[v][u] = a[u][v];
		}
		b[1][1] = 1;
		for (int i = 2; i <= n; ++ i)
			b[1][i] = 0;
		b[1][n + 1] = 0;
		for (int i = 2; i <= n; ++ i) {
			double sr(0);
			for (int j = 1; j <= n; ++ j)
				if (i != j)
					sr += 1.0 / a[i][j];
			for (int j = 1; j <= n; ++ j)
				if (i == j)
					b[i][j] = - sr;
				else
					b[i][j] = 1.0 / a[i][j];
			if (i == n)
				b[i][n + 1] = -1;
			else
				b[i][n + 1] = 0;
		}
		for (int i = 1; i <= n; ++ i) {
			int j0(i);
			for (int j = i + 1; j <= n; ++ j)
				if (abs(b[j0][i]) < abs(b[j][i]))
					j0 = j;
			if (j0 ^ i)
				for (int j = 1; j <= n + 1; ++ j)
					swap(b[i][j], b[j0][j]);
			for (int j = i + 1; j <= n; ++ j) {
				double rat(b[j][i] / b[i][i]);
				for (int k = 1; k <= n + 1; ++ k)
					b[j][k] -= b[i][k] * rat;
			}
		}
		printf("%.2lf\n", b[n][n + 1] / b[n][n]);
	}
}

BZOJ3136 [Baltic2013]brunhilda

决心写一道usaco之外的题,于是就挑中了这道。

 

为啥这编辑器有BUG,每次按回车要向下跳?

 

发现可以预处理DP。f_i表示第i个数被砍完需要最少多少刀。对于每一个i,设p_i是它的质因子里最大的一个在给定集合里的,那么它可以转移到(i+1,i+p_i)这个范围。然后f_i显然不减,所以用单调队列之类的玩意。

 

然后卡着时间限制过了。不知道前面那些秒跑的人是怎么搞的。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 10000009;
const int maxm = 100009;

int k[maxn], f[maxn], n, m, q, p[maxm], v[maxm];

void pre(int n) {
	sort(p, p + m);
	memset(k, 0, sizeof(k));
	for (int i = 0; i < m; ++ i)
		if (!i || p[i] != p[i - 1])
			for (int j = 0; j <= n; j += p[i])
				k[j] = p[i];

	static int q[maxn];
	int hd(0), tl(1);
	q[0] = f[0] = 0;
	for (int i = 1; i <= n; ++ i) {
		while (hd < tl && i >= q[hd] + k[q[hd]])
			++ hd;
		if (hd == tl) {
			f[i] = -1;
			continue;
		}
		f[i] = f[q[hd]] + 1;
		while (hd < tl && (f[i] < f[q[tl - 1]] || (f[i] == f[q[tl - 1]] && i + k[i] >= q[tl - 1] + k[q[tl - 1]])))
			-- tl;
		q[tl ++] = i;
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &m, &q);
	memset(k, 0, sizeof(k));
	for (int i = 0; i < m; ++ i)
		scanf("%d", p + i);
	n = 0;
	for (int i = 0; i < q; ++ i) {
		scanf("%d", v + i);
		if (v[i] > n)
			n = v[i];
	}
	pre(n);
	for (int i = 0; i < q; ++ i)
		if (f[v[i]] == -1)
			puts("oo");
		else
			printf("%d\n", f[v[i]]);
}

BZOJ3314 [Usaco2013 Nov]Crowded Cows

又搬blog。发现这里可以自定义css之后就果断了。

 

写道水题试试。set的应用。然后还写丑WA了两次,我没救了。

 

代码高亮是坏了吗?不要吓我。

 

决定直接用oj7的代码版了,没有高亮就算了。

#include <cstdio>
#include <algorithm>
#include <set>

using namespace std;

typedef multiset <int> rb_tree;
typedef rb_tree :: iterator rb_node;
typedef pair <int, int> cow;

const int maxn = 50009;

int n, d, x[maxn], a[maxn], r[maxn];
cow c[maxn];
rb_tree t;

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &d);
	for (int i = 1; i <= n; ++ i) {
		scanf("%d%d", x + i, a + i);
		c[i] = cow(x[i], a[i]);
		r[i] = 0;
	}
	sort(c + 1, c + n + 1);
	for (int i = 1; i <= n; ++ i) {
		x[i] = c[i]. first;
		a[i] = c[i]. second;
	}
	for (int i = 1, j = 1; i <= n; ++ i) {
		while (x[i] - x[j] > d) {
			t. erase(t. find(a[j]));
			++ j;
		}
		if (t. lower_bound(a[i] << 1) != t. end())
			++ r[i];
		t. insert(a[i]);
	}
	t. clear();
	int ans(0);
	for (int i = n, j = n; i; -- i) {
		while (x[j] - x[i] > d) {
			t. erase(t. find(a[j]));
			-- j;
		}
		if (t. lower_bound(a[i] << 1) != t. end())
			++ r[i];
		if (r[i] == 2)
			++ ans;
		t. insert(a[i]);
	}
	printf("%d\n", ans);
}