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);
}

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();
	}
}

POJ2154 Color

首先是burnside,对于旋转x下都要当作一个置换,所以总共有n个。然后第i个的不动点数量等于ngcd(n,i),然后统计一下个数是phi(gcd(n,i))个。

 

然后发现要用奇奇怪怪的东西来求phi。我用的方法是分解质因数然后DFS,跑得飞快。

 

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

using namespace std;

#define _l (long long int)

const int maxn = 50009;

int n, mod;
int tp, pn[maxn], phi[maxn];
bool pr[maxn];

void pre() {
	memset(pr, 0, sizeof(pr));
	tp = 0;
	phi[1] = 1;
	for (int i = 2; i < maxn; ++ i) {
		if (!pr[i]) {
			pn[tp ++] = i;
			phi[i] = i - 1;
		}
		for (int j = 0; j < tp && i * pn[j] < maxn; ++ j) {
			pr[i * pn[j]] = 1;
			if (i % pn[j] == 0) {
				phi[i * pn[j]] = phi[i] * pn[j];
				break;
			}
			else
				phi[i * pn[j]] = phi[i] * phi[pn[j]];
		}
	}
}

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

int fv[maxn], fc[maxn], tv, ans;

void dfs(int p, int pf, int pv) {
	if (p == tv)
		ans = (ans + _l modPow(n, pv - 1) * pf) % mod;
	else {
		dfs(p + 1, pf, pv);
		pf *= fv[p] - 1;
		pv /= fv[p];
		dfs(p + 1, pf, pv);
		for (int i = 2; i <= fc[p]; ++ i) {
			pf *= fv[p];
			pv /= fv[p];
			dfs(p + 1, pf, pv);
		}
	}
}

int calc() {
	int x(n);
	tv = 0;
	for (int i = 0; i < tp && x > 1; ++ i)
		if (x % pn[i] == 0) {
			fv[tv] = pn[i];
			fc[tv] = 0;
			while (x % pn[i] == 0) {
				++ fc[tv];
				x /= pn[i];
			}
			++ tv;
		}
	if (x > 1) {
		fv[tv] = x;
		fc[tv] = 1;
		++ tv;
	}
	ans = 0;
	dfs(0, 1, n);
	return ans;
}

int main() {
	//freopen("in.txt", "r", stdin);
	int t;
	pre();
	scanf("%d", &t);
	while (t --) {
		scanf("%d%d", &n, &mod);
		if (mod == 1)
			puts("0");
		else
			printf("%d\n", calc());
	}
}

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);
}

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]]);
}