题解 - Gemini Boy - ACM之路~

2012 Asia Chengdu Regional Contest

链接:http://acm.hdu.edu.cn/search.php?field=problem&key=2012%20Asia%20Chengdu%20Regional%20Contest%20&source=1&searchmode=source

A题(HDU 4464):无意义的题。。就贴个代码吧。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

const int maxn = 10010;

int n, cas;
char ch[maxn];

int main() {
	cas = 0;
	while (~scanf("%d", &n)) {
		int ans = 0;
		for (int i = 0; i < n; i++) {
			scanf("%s", ch);
			int len = strlen(ch);
			int sum = 0;
			for (int i = 0; i < len; i++) {
				sum += (int)ch[i];
			}
			if (ans < sum)
				ans = sum;
		}
		printf("Case %d: %d\n", ++cas, ans);
	}
	return 0;
}

B题(HDU 4465):这题是道求期望的题,还算比较简单。公式很容易得到:

但是因为组合数比较大,不能直接算。为了保证精度,可以先求log,然后再用乘方,算回来。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;

int n, cas = 0;
double p;

int main() {
	while (~scanf("%d %lf", &n, &p)) {
		printf("Case %d: ", ++cas);
		double q = 1 - p;
		if (fabs(p) < 1e-10 || fabs(q)<1e-10) {
			printf("%.6lf\n", 1.0 * n);
			continue;
		}
		double tmp = (n + 1) * log(p);
		double tem = (n + 1) * log(q);
		double ans = 0, t = 0, tt = 0, ttt = 0;
		for (int i = n + 1; i <= n + n; i++) {
			tt = t + tmp + (i-1-n) * log(q) + log(1.0+n+n-i);
			ttt = t + tem + (i-1-n) * log(p) + log(1.0+n+n-i);
			ans += exp(tt) + exp(ttt);
			t += log(1.0*i) - log(1.0*i-n);
		}
		printf("%.6lf\n", ans);
	}
	return 0;
}

HDU 4699: Editor

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4699

题意就是模拟打字,要求支持以下操作:

  • I x:在当前光标后面插入x
  • D:删除光标前面一个字符
  • L:光标向左移动一步
  • R:光标向右移动一步
  • Q k:插入前k个数字中子段和最大是多少。

这题可以用splay维护,比较简单的办法是用双向链表,维护一个ans数组,记录前i个ss数字中子段和最大是多少。对于L,R,D,I操作的时候记得更新ans数组就行了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stack>
#include <list>
using namespace std;

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

struct ListNode {
	ListNode *next;
	ListNode *pre;
	int key, sum;
};

ListNode nodes[maxn*2];
ListNode *p;
int C, id, ans[maxn];
int n, x;
char op[100];

void add(int key) {
	ListNode *q = &nodes[C++];
	q->sum = p->sum + key;
	q->key = key;
	q->pre = p;
	q->next = p->next;
	if (q->next != 0)
		q->next->pre = q;
	p->next = q;
	p = p->next;
	id++;
	ans[id] = max(p->sum, ans[id-1]);
}

void del() {
	if (p->pre == 0)
		return ;
	id--;
	ListNode *q = &nodes[C++];
	q = p->next;
	p = p->pre;
	if (q != 0)
		q->pre = p;
	p->next = q;
}

void left() {
	if (p->pre == 0)
		return ;
	id--;
	p = p->pre;
}

void right() {
	if (p->next == 0)
		return ;
	int tmp = p->sum;
	id++;
	p = p->next;
	p->sum = tmp + p->key;
	ans[id] = max(p->sum, ans[id-1]);
}

void init() {
	for (int i = 0; i < maxn; i++)
		ans[i] = -inf;
	C = 1; id = 0;
	p = &nodes[0];
	p->pre = 0;
	p->next = 0;
	p->sum = 0;
	p->key = 0;
}

void work() {
	for (int i = 0; i < n; i++) {
		scanf("%s", op);
		if (op[0] == 'I') {
			scanf("%d", &x);
			add(x);
		} else if (op[0] == 'D') {
			del();
		} else if (op[0] == 'L') {
			left();
		} else if (op[0] == 'R') {
			right();
		} else {
			scanf("%d", &x);
			printf("%d\n", ans[x]);
		}
	}
}

int main() {
	while (scanf("%d", &n) != EOF) {
		init();
		work();
	}
	return 0;
}

HDU 4705: Y

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4705

题意就是给一棵树,然后问不在一条链上的三个点有多少种。

树形DP,直接求不好求,先求其补集。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

#pragma comment(linker, "/STACK:16777216")
const int maxn = 100100;

vector<int> son[maxn];
int f[maxn], x, y;
long long n, ans;

void clear() {
	for (int i = 0; i < maxn; i++)
		son[i].clear();
	memset(f, 0, sizeof(f));
	ans = 0;
}

void dfs(int u, int fa) {
	long long tmp = 0;
	f[u] = 1;
	for (int i = 0; i < son[u].size(); i++) {
		int v = son[u][i];
		if (v == fa)
			continue;
		dfs(v, u);
		f[u] += f[v];
		ans += tmp * f[v];
		tmp += f[v];
	}
	ans += tmp * (n - f[u]);
}

void init() {
	clear();
	for (int i = 1; i < n; i++) {
		cin >> x >> y;
		son[x].push_back(y);
		son[y].push_back(x);
	}
}

void work() {
	dfs(1, -1);
	long long res = n * (n-1) * (n-2) / 6;
	cout << res - ans << endl;
}

int main() {
	while (cin >> n) {
		init();
		work();
	}
	return 0;
}

 

BZOJ 2752: [HAOI2012]高速公路(road) (线段树)

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2752

概率背景的题,不过稍微想想,稍微推一推,就能得到公式。然后注意到只要用线段树维护3个sum就行了,sum1 费用和, sum2 下标乘以费用的和 sum3下标的平方乘以费用的和

PS:。。。在写线段树的时候被一个傻逼问题bug住了好久,,调了好久。好吧 我就是煞笔

2版线段树的代码。

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn = 100100;

long long sumo[maxn], sump[maxn];

struct SegNode {
	int left, right;
	long long sum1, sum2, sum3;
	long long add;

	int mid() {
		return (left + right) >> 1;
	}

	long long size() {
		return (right - left + 1);
	}
};

struct SegmentTree {

	SegNode tree[maxn*5];

	void pushUp(int idx) {
		tree[idx].sum1 = tree[idx<<1].sum1 + tree[idx<<1|1].sum1;
		tree[idx].sum2 = tree[idx<<1].sum2 + tree[idx<<1|1].sum2;
		tree[idx].sum3 = tree[idx<<1].sum3 + tree[idx<<1|1].sum3;
	}

	void pushDown(int idx) {
		if (tree[idx].add) {
			long long add = tree[idx].add;
			int right1 = tree[idx<<1].right;
			int left1 = tree[idx<<1].left;
			int right2 = tree[idx<<1|1].right;
			int left2 = tree[idx<<1|1].left;
			tree[idx].add = 0;
			tree[idx<<1].sum1 += tree[idx<<1].size() * add;
			tree[idx<<1|1].sum1 += tree[idx<<1|1].size() * add;
			tree[idx<<1].sum2 += (sumo[right1] - sumo[left1-1]) * add;
			tree[idx<<1|1].sum2 += (sumo[right2] - sumo[left2-1]) * add;
			tree[idx<<1].sum3 += (sump[right1] - sump[left1-1]) * add;
			tree[idx<<1|1].sum3 += (sump[right2] - sump[left2-1]) * add;
			tree[idx<<1].add += add;
			tree[idx<<1|1].add += add;
		}
	}

	void build(int left, int right, int idx) {
		tree[idx].left = left;
		tree[idx].right = right;
		tree[idx].sum1 = 0;
		tree[idx].sum2 = 0;
		tree[idx].sum3 = 0;
		tree[idx].add = 0;
		if (left == right)
			return ;
		int mid = tree[idx].mid();
		build(left, mid, idx<<1);
		build(mid+1, right, idx<<1|1);
	}

	void update(int left, int right, int idx, long long value) {
		if (tree[idx].left == left && tree[idx].right == right) {
			tree[idx].add += value;
			tree[idx].sum1 += tree[idx].size() * value;
			tree[idx].sum2 += (sumo[right] - sumo[left-1]) * value;
			tree[idx].sum3 += (sump[right] - sump[left-1]) * value;
			return ;
		}
		pushDown(idx);
		int mid = tree[idx].mid();
		if (right <= mid)
			update(left, right, idx<<1, value);
		else if (left > mid)
			update(left, right, idx<<1|1, value);
		else {
			update(left, mid, idx<<1, value);
			update(mid+1, right, idx<<1|1, value);
		}
		pushUp(idx);
	}

	long long query(int left, int right, int idx, int L, int R) {
		if (tree[idx].left == left && tree[idx].right == right) {
			//printf("%d %d %d\n", idx, left, right);
			return (R + L - 1) * tree[idx].sum2 - tree[idx].sum3 - 
				((long long)L * R - R) * tree[idx].sum1;
		}
		pushDown(idx);
		int mid = tree[idx].mid();
		if (right <= mid)
			return query(left, right, idx<<1, L, R);
		else if (left > mid)
			return query(left, right, idx<<1|1, L, R);
		else {
			return (query(left, mid, idx<<1, L, R) + query(mid+1, right, idx<<1|1, L, R));
		}
	}	
	
};
SegmentTree tree;

int N, M;

long long gcd(long long a, long long b) {
	return b ? gcd(b, a % b) : a;
}

void init() {
	memset(sumo, 0, sizeof(sumo));
	memset(sump, 0, sizeof(sump));
	for (int i = 1; i < maxn; i++)
		sumo[i] = sumo[i-1] + i;
	for (int i = 1; i < maxn; i++)
		sump[i] = sump[i-1] + (long long)i * i;
	scanf("%d %d", &N, &M);
	tree.build(1, N, 1);
}

void work() {
	char op[19];
	for (int i = 0; i < M; i++) {
		scanf("%s", op);
		if (op[0] == 'C') {
			int x, y;
			long long z;
			scanf("%d %d %lld", &x, &y, &z);
			tree.update(x, y-1, 1, z);
		} else {
			int x, y;
			scanf("%d %d", &x, &y);
			long long tmp = tree.query(x, y, 1, x, y);
			long long tem = (long long)(y - x + 1) * (y - x) / 2;
			long long ans = gcd(tmp, tem);
			printf("%lld/%lld\n", tmp / ans, tem / ans);
		}
	}
}

int main() {
	init();
	work();
	return 0;
}
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn = 100100;

long long sumo[maxn], sump[maxn];

struct SegNode {
	int left, right;
	long long sum1, sum2, sum3;
	long long add;
};

struct SegmentTree {

	SegNode tree[maxn*5];

	void pushUp(int idx) {
		tree[idx].sum1 = tree[idx<<1].sum1 + tree[idx<<1|1].sum1;
		tree[idx].sum2 = tree[idx<<1].sum2 + tree[idx<<1|1].sum2;
		tree[idx].sum3 = tree[idx<<1].sum3 + tree[idx<<1|1].sum3;
	}

	void pushDown(int idx, int left, int right) {
		if (tree[idx].add) {
			long long add = tree[idx].add;
			tree[idx].add = 0;
			tree[idx].sum1 += (right - left + 1) * add;
			tree[idx].sum2 += (sumo[right] - sumo[left-1]) * add;
			tree[idx].sum3 += (sump[right] - sump[left-1]) * add;
			if (left == right) return ;
			tree[idx<<1].add += add;
			tree[idx<<1|1].add += add;
		}
	}

	void update(int L, int R, int left, int right, int idx, long long value) {
		pushDown(idx, L, R);
		if (L >= left && R <= right) {
			tree[idx].add += value;
			return ;
		}
		int mid = (L + R) >> 1;
		if (left <= mid)
			update(L, mid, left, right, idx<<1, value);
		if (right >= mid+1)
			update(mid+1, R, left, right, idx<<1|1, value);
		pushDown(idx<<1, L, mid);
		pushDown(idx<<1|1, mid+1, R);
		pushUp(idx);
	}

	long long query(int L, int R, int left, int right, int idx) {
		pushDown(idx, L, R);
		if (L >= left && R <= right) {
			printf("%d %d %d\n", idx, left, right);
			return (right + left - 1) * tree[idx].sum2 - tree[idx].sum3 - 
				((long long)left * right - right) * tree[idx].sum1;
		}
		int mid = (L + R) >> 1;
		long long ans = 0;
		if (left <= mid)
			ans += query(L, mid, left, right, idx<<1);
		if (right >= mid + 1)
			ans += query(mid+1, R, left, right, idx<<1|1);
		return ans;
	}		
	
};
SegmentTree tree;

int N, M;

long long gcd(long long a, long long b) {
	return b ? gcd(b, a % b) : a;
}

void init() {
	memset(sumo, 0, sizeof(sumo));
	memset(sump, 0, sizeof(sump));
	for (int i = 1; i < maxn; i++)
		sumo[i] = sumo[i-1] + i;
	for (int i = 1; i < maxn; i++)
		sump[i] = sump[i-1] + (long long)i * i;
	scanf("%d %d", &N, &M);
}

void work() {
	char op[19];
	for (int i = 0; i < M; i++) {
		scanf("%s", op);
		if (op[0] == 'C') {
			int x, y;
			long long z;
			scanf("%d %d %lld", &x, &y, &z);
			tree.update(1, N, x, y-1, 1, z);
		} else {
			int x, y;
			scanf("%d %d", &x, &y);
			long long tmp = tree.query(1, N, x, y, 1);
			long long tem = (long long)(y - x + 1) * (y - x) / 2;
			long long ans = gcd(tmp, tem);
			printf("%lld/%lld\n\n", tmp / ans, tem / ans);
		}
	}
}

int main() {
	init();
	work();
	return 0;
}

BZOJ 3251: 树上三角形

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3251

题意就是给一棵树,点权,然后查询一条链上是否有3个点的点权能组成一个三角形。

注意到三角形任意2边之和大于第三边,然后最坏的情况类似于斐波那契数列,大约4 50左右的时候,就会超int。所以当链的长度大于45的时候,返回true,否则就暴力判断是否可行。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

const int maxn = 101110;

int N, Q;
vector<int> next[maxn];
long long val[maxn];
int par[maxn], dep[maxn];

void dfs(int u) {
	for (int i = 0; i < next[u].size(); i++) {
		int v = next[u][i];
		//if (dep[v]) continue;
		dep[v] = dep[u] + 1;
		dfs(v);
	}
}

bool check(int x, int y) {
	int cnt = 0;
	long long num[55];
	for ( ; dep[x] > dep[y]; x = par[x]) {
		num[cnt++] = val[x];
		if (cnt > 45) return true;
	}
	for ( ; x != y; x = par[x], y = par[y]) {
		num[cnt++] = val[x];
		num[cnt++] = val[y];
		if (cnt > 45) return true;
	}
	num[cnt++] = val[y];
	if (cnt < 3) 
		return false;
	sort(num, num + cnt);
	for (int i = 0; i + 2 < cnt; i++) {
		if (num[i] + num[i+1] > num[i+2])
			return true;
	}
	return false;
}

void init() {
	for (int i = 0; i < maxn; i++) {
		next[i].clear();
		dep[i] = 0;
		val[i] = 0;
		par[i] = 0;
	}
	par[1] = -1;
	scanf("%d %d", &N, &Q);
	for (int i = 1; i <= N; i++)
		scanf("%d", &val[i]);
	for (int i = 1; i < N; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		par[y] = x;
		next[x].push_back(y);
	}
	dep[1] = 1;
	dfs(1);
}

void work() {
	for (int i = 0; i < Q; i++) {
		int opt, x, y;
		scanf("%d %d %d", &opt, &x, &y);
		if (opt == 1)
			val[x] = y;
		else {
			if (dep[x] < dep[y])
				swap(x, y);
			if (x == y || x == par[y] || y == par[x]) {
				puts("N");
				continue;
			}
			if (check(x, y) == true)
				puts("Y");
			else puts("N");
		}
	}
}

int main() {
	init();
	work();
	return 0;
}




Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee