[icpcarchive 6045 – Alien Abduction] Scapegoat Tree + KD Tree

今年雅加达的H题。OJ上一直没人过,然后自己写了一下,线段树套set TLE了几回,然后想起之前一直没敢写的Scapegoat Tree + KD Tree,试了一下居然AC了。
^_^哈哈,不得不说,这种拿题库FirstBlood的感觉真是爽。
传送门
【题意】

给定n(n<=50000)个在二维平面上的整点,坐标保证0<=x<=w, 0<=y<=h。再给定q(q<=50000)个操作,每个操作为给定一个点$$(x_i, y_i)$$,将离$$(x_i, y_i)$$曼哈顿距离小于等于$$E_i$$的点的坐标都作一个奇葩的变换。 $$X_i' = ((X_i * a) + (Y_i * b) + (i * c)) mod W$$ $$Y_i' = ((X_i * d) + (Y_i * e) + (i * f)) mod H$$ 然后在q次操作以后,把n个点的坐标输出来。题目保证,总共的变换次数不超过50000次。

【算法】
因为总共变换的次数不超过50000次,所以问题就在于如何快速地选中离$$(x_i, y_i)$$曼哈顿距离小于等于$$E_i$$的点。
显然满足条件的区域是一个旋转了45°的正方形,所以我们只要把坐标旋转45°就好了。也就是这样子:$$x’=x+y, y’=x-y+h$$。
好了,这样子问题就转化成如何快速选中一个所有边都平行于坐标轴的矩形内部点的问题。
显然这题直接用线段树套个set可以做到$$O(\log^2 n)$$ per operation. 但是无情地TLE……
尝试使用KD Tree解决。
显然把点都插入KDTree,然后询问就$$O(\sqrt n)$$的了。
但是有一个问题,选中这些点以后,要将其从KDTree中删除,将坐标变换以后再插入。而KDTree是一个类似二叉搜索树的数据结构,但坑爹的是不能旋转,这样若干次操作以后,KDTree就会退化。
打着想水过的念头写了一下,果断TLE。
一个可行的解决方案是操作了$$\sqrt n$$个结点以后,就rebuild整棵树。但这样整体复杂度就是$$O(n\sqrt n)$$的。
之前在ACM_DIY群问了一下这个问题,数据结构帝CLJ说Scapegoat_tree可以不旋转只rebuild就达到均摊$$\log n$$的复杂度。去膜拜过wiki以后,发现确实可以做到。
于是写了一下,居然就过了。
所谓Scapegoat_tree,就是如果左右子树中的某个size超过了$$\alpha$$*this->size,那么就rebuild这棵子树。虽然看起来很傻X,但是确实可以证明复杂度是lg级别的。当然$$0.5 < \alpha < 1$$。 而所谓KDTree就是在第i层的子树都取其关于第i % K维的中位数的那个结点作为根,然后左右子树递归下去。 不明白的建议看看英文wiki。 【时空分析】 最坏的计算量是$$50000 * \sqrt{50000}$$级别的。 空间复杂度是O(n)的。 【其它】 截个图留念一下,这种现在只有我过掉这题的感觉真是好爽 :Delighted:

【代码】

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
template  void checkmin(T &t,T x){if (x < t) t = x;}
template  void checkmax(T &t,T x){if (x > t) t = x;}
template  void _checkmin(T &t, T x){if (t == -1) t = x; if (x < t) t = x;}
template  void _checkmax(T &t, T x){if (t == -1) t = x; if (x > t) t = x;}
typedef pair  PII;
typedef pair  PDD;
typedef long long lld;
#define foreach(it,v) for (__typeof((v).begin()) it = (v).begin();it != (v).end();it++)
#define DEBUG(a) cout << #a" = " << (a) << endl;
#define DEBUGARR(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }
const int N = 105555;
const int INF = 1000000000;
const double mul = 0.7;
int Tc;
int n, q, w, h;

struct point {
	int x, y, id, rx, ry;
	point() {}
	void trans() {
		rx = x + y;
		ry = x - y + h;
	}
}p[N], tp[N];

int pt, ptr_t;
struct Node {
	int size;
	bool exist, div;
	point mid;
	Node *lc, *rc;
	Node() {size = exist = 0;}

	bool judge() {
		return !size || lc->size > mul * size || rc->size > mul * size;
	}
}pool[N * 5], *root, *null = &pool[0], *ptr[N];

inline bool cmpx(const point &a, const point &b)  { return a.rx < b.rx || (a.rx == b.rx && a.id < b.id); }
inline bool cmpy(const point &a, const point &b)  { return a.ry < b.ry || (a.ry == b.ry && a.id < b.id); }
inline bool cmp(const point &a, const point &b, const bool &div) { return div ? cmpy(a, b) : cmpx(a, b); }

Node *build(point *a, int l, int r, bool div, bool type = 1) {
	if (l > r) return null;
	int mid = (l + r) / 2;
	Node *p = type ? &pool[pt++] : ptr[--ptr_t];
	nth_element(a + l, a + mid,  a + r + 1, div ? cmpy : cmpx);
	p->div = div;
	p->mid = a[mid];
	p->exist = 1;
	p->lc = build(a, l, mid - 1, !div, type);
	p->rc = build(a, mid + 1, r, !div, type);
	p->size = p->lc->size + p->rc->size + 1;
	return p;
}

void dfs(Node *p) {
	if (p == null) return;
	if (p->exist) {
		tp[ptr_t] = p->mid;
		ptr[ptr_t++] = p;
	}
	dfs(p->lc);
	dfs(p->rc);
}

void rebuild(Node *&p) {
	ptr_t = 0;
	dfs(p);
	p = build(tp, 0, ptr_t - 1, p->div, 0);
}


void insert(Node *&p, point a, bool div, bool re = 0) {
	if (p == null) {
		p = &pool[pt++];
		p->div = div;
		p->exist = 1;
		p->size = 1;
		p->mid = a;
		p->lc = p->rc = null;
	} else {
		p->size++;
		bool tag = p->judge();
		if (cmp(a, p->mid, div)) {
			insert(p->lc, a, !div, re | tag);
		} else {
			insert(p->rc, a, !div, re | tag);
		}
		if (!re && tag) rebuild(p);
	}
}

vector  vec;

void getvec(Node *&p, int lx, int ly, int rx, int ry, bool re = 0) {
	if (p->size == 0) return;
	if (p->exist && lx <= p->mid.rx && p->mid.rx <= rx && ly <= p->mid.ry && p->mid.ry <= ry) {
		vec.push_back(p->mid.id);
		p->exist = 0;
		p->size--;
	}
	bool tag = p->judge();
	if (p->div ? ly <= p->mid.ry : lx <= p->mid.rx) getvec(p->lc, lx, ly, rx, ry, re | tag);
	if (p->div ? ry >= p->mid.ry : rx >= p->mid.rx) getvec(p->rc, lx, ly, rx, ry, re | tag);
	p->size = p->lc->size + p->rc->size + p->exist;
	if (!re && tag) rebuild(p);
}

int main() {
	scanf("%d", &Tc);
	for (int ri = 1; ri <= Tc; ri++) {
		pt = 1;
		scanf("%d%d%d%d", &n, &q, &w, &h);
		for (int i = 1; i <= n; i++) {
			scanf("%d%d", &p[i].x, &p[i].y);
			p[i].id = i;
			p[i].trans();
			tp[i] = p[i];
		}
		root = build(tp, 1, n, 0);
		for (int i = 0; i < q; i++) {
			int x, y, E, a, b, c, d, e, f;
			scanf("%d%d%d%d%d%d%d%d%d", &x, &y, &E, &a, &b, &c, &d, &e, &f);
			int lx, ly, rx, ry;
			lx = x + y - E;
			ly = x - y - E + h;
			rx = x + y + E;
			ry = x - y + E + h;
			checkmax(lx, 0);
			checkmax(ly, 0);
			checkmin(rx, w + h);
			checkmin(ry, w + h);
			vec.clear();
			getvec(root, lx, ly, rx, ry);
			foreach (it, vec) {
				lld ix = p[*it].x;
				lld iy = p[*it].y;
				int tx = ((ix * a) + (iy * b) + ((lld)(*it) * c)) % w;
				int ty = ((ix * d) + (iy * e) + ((lld)(*it) * f)) % h;
				p[*it].x = tx;
				p[*it].y = ty;
				p[*it].trans();
				insert(root, p[*it], 0);
			}
		}
		printf("Case #%d:\n", ri);
		for (int i = 1; i <= n; i++) {
			printf("%d %d\n", p[i].x, p[i].y);
		}
	}
}

World Final Training 5——2011-2012 Petrozavodsk Summer Training Camp, Kyiv + Kharkov NU Contest

传送门
Board

然后这是我们

A题TLE到死,然后发现status里面有好多人都是2秒多3秒过的……我们1秒就T了,于是问了一下KADR,得到的回答是:

Hi,Thanks for pointing out this problem! I’ve asked MikeMirzayanov about it and he said there was a bug in the system which was fixed about a month ago. Apparently time limit has been set to 5 seconds for a few months and some people got AC with nonoptimal solutions. I think it is not fair to rejudge their solutions now, but taking into account the fact that this is only a training in the gym, from now on the time limit will be set to 1 second.Problem statement says that the time limit is 2 sec for this problem. However, Codeforces judging servers are faster than servers used on the original contest in Petrozavodsk training camp. That’s why some time limits on Codeforces differ from the ones in statement. You can find the correct time limits in “Problems” tab on the contest page. Sorry for inconvenience.

-.-反正是练习嘛,就这样吧。
记一下A题

先给定一个素数P(P<=10^8),再给定Q(Q<=10^4)个询问,每个询问给定$$a_i, b_i$$,问$$a_i^x = b_i (mod P)$$的最小解x是多少。

如果啥都不看显然是经典的离散对数问题。
Baby-step Giant-step是其中一个经典解法,但是这个复杂度是$$O(\sqrt{P})$$的。乘起来到10^8还是会T。
用原根$$g^s, g^t$$去分别替掉a和b的话,就变成了$$(g^s)^x = g^t (mod P)$$,也就变成解这个同余方程$$s*x = t (mod (P – 1))$$,这个可以扩展gcd然后算,就不说了。
然后变成怎么求a, b对原根g的指标(也就是求s和t)。这就又回到了离散对数问题,虽然想呵呵,但是仔细想想还是有点不一样的。那就是变成了询问2Q次$$g^x = a (mod P)$$的解,这个底数被固定了。
最开始我们是想打张指标表,但是单纯的O(P)还是会T。
如果TL是5s的话,这里已经足够通过了。
于是我们还是得用Baby-step Giant-step,但是得注意到这个分块的精髓在于让两边的计算量接近。
那么假设我们每个块是S这么大,BSGS的时候就要先打一张S这么大的表,都塞进hash table里面。
然后对于每一个求指标的询问,我们需要扫P/S次,每次都要到hash table里面找一找。
于是打表的复杂度是S,处理询问的复杂度是Q*P/S,为了使总复杂度最小,两边取等号,S = Q*P/S,于是$$S=\sqrt{Q*P}$$,算起来就是S=10^6。
总计算量就是10^6级别了。
于是速度刷到了第一……

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
template  void checkmin(T &t,T x){if (x < t) t = x;}
template  void checkmax(T &t,T x){if (x > t) t = x;}
template  void _checkmin(T &t, T x){if (t == -1) t = x; if (x < t) t = x;}
template  void _checkmax(T &t, T x){if (t == -1) t = x; if (x > t) t = x;}
typedef pair  PII;
typedef pair  PDD;
typedef long long lld;
#define foreach(it,v) for (__typeof((v).begin()) it = (v).begin();it != (v).end();it++)
#define DEBUG(a) cout << #a" = " << (a) << endl;
#define DEBUGARR(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }
const int N = 100005;
const int block_size = 800000;
int P, Q, ori;
int a[N], b[N];
map  ind;
lld inv;

namespace HashTable {
	const int HashMod = 10000007;
	int pt;
	struct edge {
		int x, y;
		edge *next;
	}pool[block_size + 5], *ls[HashMod];

	void add(int x, int y) {
		int i = x % HashMod;
		edge *t = &pool[pt++];
		t->x = x;
		t->y = y;
		t->next = ls[i];
		ls[i] = t;
	}

	int in(int x) {
		for (edge *t = ls[x % HashMod]; t; t = t->next)
			if (t->x == x) return t->y;
		return -1;
	}
}

void gao1() {
    for (int i = 0; i < Q; i++) {
        lld t = 1, ret = -1;
        for (int j = 0; j < P; j++) {
            if (t == b[i]) {
                ret = j;
                break;
            } else {
                t = t * a[i] % P;
            }
        }
        cout << ret << endl;
    }
}

int pow_mod(lld a, int b) {
    lld ret = 1;
    for ( ;b; b >>= 1) {
        if (b & 1) ret = ret * a % P;
        a = a * a % P;
    }
    return ret;
}

lld extgcd(lld a, lld b, lld &x, lld &y) {
    lld t, ret;
    if (!b) {
        x = 1;
        y = 0;
        return a;
    }
    ret = extgcd(b, a % b, x, y);
    t = x;
    x = y;
    y = t - a / b * y;
    return ret;
}

int getind(int x) {
	if (ind.count(x)) return ind[x];
	int t = x;
	for (int i = 0; i < P; i += block_size) {
		int ret = HashTable::in(x);
		if (ret != -1) {
			ind[t] = i + ret;
			return i + ret;
		}
		x = x * inv % P;
	}
	return -1;
}

void init() {
	lld t = 1;
	for (int i = 0; i < block_size && i < P; i++) {
		if (HashTable::in(t) == -1) {
			HashTable::add(t, i);
		}
		t = t * ori % P;
	}
	inv = pow_mod(t, P - 2);
}


int gao2() {
	vector  vec;
    int phi = P - 1;
    for (int i = 2; i * i <= phi; i++) {
        if (phi % i == 0) {
            vec.push_back(i);
            vec.push_back(phi / i);
        }
    }
    for (int i = 2; ; i++) {
        bool flag = 1;
        foreach (it, vec) {
            if (pow_mod(i, *it) == 1) {
                flag = 0;
                break;
            }
        }
        if (flag) {
            ori = i;
            break;
        }
    }
	init();
    for (int i = 0; i < Q; i++) {
        if (a[i] == 0) {
            int ret = -1;
            if (b[i] == 0) ret = 1;
            if (b[i] == 1) ret = 0;
            printf("%d\n", ret);
            continue;
        }
        if (b[i] == 0) {
            printf("-1\n");
            continue;
        }
        if (a[i] == 1) {
            printf("%d\n", b[i] == 1 ? 0 : -1);
        } else if (b[i] == 1) {
            printf("0\n");
        } else {
            lld s = getind(a[i]), t = getind(b[i]), x, y;
            lld g = extgcd(s, phi, x, y);
            if (t % g != 0) {
                printf("-1\n");
            } else {
                lld s=phi/g,ans;
                t/=g;
                x = (x % s+s) %s;
                x=x*t%phi;
                ans=x;
                x = (phi - x) / s * s + x;
                for (int i = 0; i < 5; i++) {
                    x += s;
                    x %= phi;
                    if (x < ans) ans = x;
                }
                cout << ans << endl;
            }
        }
    }
}


int main(){
    freopen("alot.in", "r", stdin);
    freopen("alot.out", "w", stdout);
	scanf("%d%d", &P, &Q);
	for (int i = 0; i < Q; i++) {
		scanf("%d%d", &a[i], &b[i]);
	}
	if (P <= 2) {
		gao1();
	} else {
		gao2();
	}
}

[sgu 508] 逆概率

传送门

给定n个球,它们要么黑,要么白。一开始不知道里面有几个是黑的,但是黑球的数目是等概率分布的。然后等概率地取里面的a+b个球,结果发现其中有a个白球,b个黑球。然后要求出原来有i(i = 1..n)个黑球的概率。

zYc说用xxxx公式……反正没听懂,然后比赛时很脑残地不会搞。
后来自己想了一下,其实可以看成一共有$$C_n^{a + b} * (n + 1)$$种等概率的情况。前面的组合数是就是这a+b个球的取法,后面的n+1是黑球的数目。
然后既然结果是(a, b),那么就分别算出$$C_n^{a + b} * (n + 1)$$种情况下,一共有多少种是符合(a, b)的,记为sum。然后有i个黑球的概率就是看有i个黑球时,可以对(a, b)造成多少个等概率事件的“贡献”,最后除以sum就得解。
现在看起来好像很简单的样子。
真正的勇士要敢于直面自己当时是SB这样血淋淋的事实……

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
template  void checkmin(T &t,T x){if (x < t) t = x;}
template  void checkmax(T &t,T x){if (x > t) t = x;}
template  void _checkmin(T &t, T x){if (t == -1) t = x; if (x < t) t = x;}
template  void _checkmax(T &t, T x){if (t == -1) t = x; if (x > t) t = x;}
typedef pair  PII;
typedef pair  PDD;
typedef long long lld;
#define foreach(it,v) for (__typeof((v).begin()) it = (v).begin();it != (v).end();it++)
#define DEBUG(a) cout << #a" = " << (a) << endl;
#define DEBUGARR(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }
const int N = 55;
int n, a, b, p;
lld C[N][N];
double u[N];

int main(){
#ifdef cwj
	freopen("in", "r", stdin);
#endif
	scanf("%d%d%d%d", &n, &a, &b, &p);
	if (p == 0) {
		puts("0 0");
		return 0;
	}
	C[0][0] = 1;
	for (int i = 1; i <= 50; i++) {
		for (int j = 0; j <= i; j++) {
			C[i][j] = (j ? C[i - 1][j - 1] : 0) + C[i - 1][j];
		}
	}
	lld sum = 0;
	for (int i = 0; i <= n; i++) {
		sum += C[i][a] * C[n - i][b];
	}
	for (int i = 0; i <= n; i++) {
		u[i] = C[i][a] * C[n - i][b] / (double)sum;
//		printf("%d %.12lf\n", i, u[i]);
	}
	int bi = -1, bj = -1;
	for (int i = 0; i <= n; i++) {
		double ret = 0;
		for (int j = i; j <= n; j++) {
			ret += u[j];
			if (ret + 1e-14 >= p / 100.0) {
				if (bi == -1 || j - i < bj - bi) {
					bi = i;
					bj = j;
				}
			}
		}
	}
	printf("%d %d\n", bi, bj);
}

Codeforces Round #157

传送门
比赛的时候就很慢很慢地搞出前三题……然后rating加了5,囧
记一下自己犯圡的地方好了。
258A
不说了。
258B
一开始看错题了,但是数位dp的部分还是写得很快的。后来发现看错题以后,感觉后面那部分很难算,用dp写了很久(组合计数也太不熟练了),总算pass了。
值得记录的一点是即使是分类相同的项里面取,也应该看成有顺序的,最后再乘6!这样的算法比较科学。
实际上最后那一部分10^7的dfs就可以了。对数据范围不够敏感……作为一个ACMer,能暴力就暴力是基本原则。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
template  void checkmin(T &t,T x){if (x < t) t = x;}
template  void checkmax(T &t,T x){if (x > t) t = x;}
template  void _checkmin(T &t, T x){if (t == -1) t = x; if (x < t) t = x;}
template  void _checkmax(T &t, T x){if (t == -1) t = x; if (x > t) t = x;}
typedef pair  PII;
typedef pair  PDD;
typedef long long lld;
#define foreach(it,v) for (__typeof((v).begin()) it = (v).begin();it != (v).end();it++)
#define DEBUG(a) cout << #a" = " << (a) << endl;
#define DEBUGARR(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }
const int N = 12;
const int Mod = 1000000007;
int n;
int f[N][N][2];
char s[N];

int A6(int x) {
	if (x < 6) return 0;
	long long ret = 1;
	for (int i = 0; i < 6; i++) {
		ret = ret * (x - i);
		ret %= Mod;
	}
	return ret;
}

int Pow(long long a, int b) {
	long long ret = 1;
	while (b) {
		if (b & 1) {
			ret = ret * a;
			ret %= Mod;
		}
		a *= a;
		a %= Mod;
		b >>= 1;
	}
	return ret;
}

int C(int m, int n) {
	if (m < n) return 0;
	long long ret = 1;
	for (int i = 0; i < n; i++) {
		ret = ret * (m - i);
		ret %= Mod;
		ret = ret * Pow(i + 1, Mod - 2);
		ret %= Mod;
	}
	return ret;
}

int main() {
	scanf("%s", s + 1);
	n = strlen(s + 1);
	memset(f, 0, sizeof(f));
	f[0][0][1] = 1;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			for (int o = 0; o < 2; o++) {
				for (int k = 0; k <= (o ? s[i + 1] - '0' : 9); k++) {
					int &t = f[i + 1][j + (k == 4 || k == 7)][o && (k == s[i + 1] - '0')];
					t += f[i][j][o];
				}
			}
		}
	}
	f[n][0][0] += Mod - 1;
	f[n][0][0] %= Mod;
	int sum = 0;
	int ans = 0;
	int g[25][25][7];
	memset(g, 0, sizeof(g));
	g[0][0][0] = 1;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j <= n; j++) {
			for (int k = 0; k <= 6; k++) {
				g[i + 1][j][k] += g[i][j][k];
				g[i + 1][j][k] %= Mod;
				for (int o = 1; k + o <= 6 && o * i + j <= n && o <= f[n][i][0] + f[n][i][1]; o++) {
					g[i + 1][j + o * i][k + o] += 1LL * g[i][j][k] * C(f[n][i][0] + f[n][i][1], o) % Mod;
					g[i + 1][j + o * i][k + o] %= Mod;
				}
			}
		}
	}
	for (int i = 0; i <= n; i++) {
		ans += 1LL * sum * (f[n][i][0] + f[n][i][1]) % Mod;
		ans %= Mod;
		sum += g[n][i][6] * 720LL % Mod;
//		printf("%d %d\n", i, g[n][i][6]);
		sum %= Mod;
	}
	cout << ans << endl;
}

258C
直接枚举约数然后每个容斥统计。一开始觉得复杂度有点悬,好像是$$O(n\sqrt{n} \log n)$$的,但是突然就想起来[1,n]之间的约数个数应当是$$O(n\log n)$$级别的(参见筛法),所以整个算法应该是$$O(n\sqrt{n} + n \log^2 n)$$的

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
template  void checkmin(T &t,T x){if (x < t) t = x;}
template  void checkmax(T &t,T x){if (x > t) t = x;}
template  void _checkmin(T &t, T x){if (t == -1) t = x; if (x < t) t = x;}
template  void _checkmax(T &t, T x){if (t == -1) t = x; if (x > t) t = x;}
typedef pair  PII;
typedef pair  PDD;
typedef long long lld;
#define foreach(it,v) for (__typeof((v).begin()) it = (v).begin();it != (v).end();it++)
#define DEBUG(a) cout << #a" = " << (a) << endl;
#define DEBUGARR(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

const int N = 105555;
const int Mod = 1000000007;
int n;
int a[N];
int sum[N];
int num[N];

int Pow(long long a, int b) {
	long long ret = 1;
	while (b) {
		if (b & 1) ret = ret * a % Mod;
		a = a * a % Mod;
		b >>= 1;
	}
	return ret;
}

int main(){
	scanf("%d", &n);
	set  Set;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		sum[a[i]]++;
	}
	for (int i = 100000; i >= 0; i--) {
		sum[i] = sum[i + 1] + sum[i];
	}
	long long ans = 0;
	for (int i = 1; i <= 100000; i++) {
		if (sum[i]) {
			vector  vec;
			for (int j = 1; j * j <= i; j++) {
				if (i % j == 0) {
					vec.push_back(j);
					if (j * j != i) vec.push_back(i / j);
				}
			}
			num[i] = vec.size() + 1;
			sort(vec.begin(), vec.end());
			long long ret = 1;
			for (int j = (int)(vec.size()) - 2; j >= 0; j--) {
				int now = sum[vec[j]];
				now -= sum[vec[j + 1]];
				ret = ret * Pow(j + 1, now) % Mod;
			}
			long long tmp = Pow(vec.size(), sum[vec[vec.size() - 1]]) - Pow((int)vec.size() - 1, sum[vec[vec.size() - 1]]);
			tmp += Mod;
			tmp %= Mod;
//			cout << i << " " << ret * tmp << endl;
			ans += 1LL * ret * tmp % Mod;
			ans %= Mod;
		}
	}
	cout << ans << endl;
	return 0;
}

258D
这种算数目的数学期望题……
其实就利用概率论与数理统计之前学到的那个将$$E(x)$$分解为若干个$$E(x_i)$$之和,然后分别算期望,叠加起来就好了。
这些随机变量$$x_i$$之间有关无关都是没有关系的。
至于为什么能这么搞,那是因为E(x)本来计算公式就是线性的……

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
template  void checkmin(T &t,T x){if (x < t) t = x;}
template  void checkmax(T &t,T x){if (x > t) t = x;}
template  void _checkmin(T &t, T x){if (t == -1) t = x; if (x < t) t = x;}
template  void _checkmax(T &t, T x){if (t == -1) t = x; if (x > t) t = x;}
typedef pair  PII;
typedef pair  PDD;
typedef long long lld;
#define foreach(it,v) for (__typeof((v).begin()) it = (v).begin();it != (v).end();it++)
#define DEBUG(a) cout << #a" = " << (a) << endl;
#define DEBUGARR(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }
const int N = 1005;
int n, m;
int a[N];
double p[N][N];

int main(){
#ifdef cwj
	freopen("in", "r", stdin);
#endif
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			p[i][j] = a[i] > a[j];	
		}
	}
	for (int i = 0; i < m; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		for (int j = 1; j <= n; j++) {
			if (j != x && j != y) {
				p[j][x] = p[j][y] = (p[j][x] + p[j][y]) / 2;
				p[x][j] = p[y][j] = (p[x][j] + p[y][j]) / 2;
			}
		}
		p[x][y] = p[y][x] = 0.5;
	}
	double ans = 0;
	for (int i = 1; i < n; i++) {
		for (int j = i + 1; j <= n; j++) {
			ans += p[i][j];
		}
	}
	printf("%.12lf\n", ans);
}

258E
对这种不要求在线算法的情况,设计一个合理的计算顺序是至关重要,离线算法相对于在线算法的一个很大的优势就在于计算顺序可以按自己调。
这里也不例外。
首先将子树转化为dfs序中连续一段已经是很old的技巧了,然后按dfs序列从左往右扫,每个询问就只进栈两次再出栈两次。
这样只要用线段树统计出目前被覆盖的点的数目就可以了。这个统计的方法类似于用线段树求矩形并的面积。
具体方法就是记录当前区间的最小值以及这个最小值的数目就可以轻松统计出来了。

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int N = 100005;
int n, m, ot;
int st[N], ed[N];
int order[N];
vector  E[N];

void dfs(int x, int fa) {
	order[++ot] = x;
	st[x] = ot;
	for (vector ::iterator it = E[x].begin(); it != E[x].end(); it++) {
		if (*it != fa) {
			dfs(*it, x);
		}
	}
	ed[x] = ot;
}

	struct Node {
		int l, r, minv, num, d;
		Node *lc, *rc;
		Node(){}
		Node(int l, int r):l(l), r(r) { num = r - l + 1; minv = d = 0; lc = rc = NULL; }
		bool fit(int _l, int _r) { return _l <= l && r <= _r; }
		void pushdown() {
			if (d) {
				minv += d;
				if (lc) {
					lc->d += d;
					rc->d += d;
				}
				d = 0;
			}
		}

		void update() {
			if (lc) {
				minv = min(lc->minv + lc->d, rc->minv + rc->d);
				num = 0;
				if (lc->minv + lc->d == minv) num += lc->num;
				if (rc->minv + rc->d == minv) num += rc->num;
			}
		}
	}tr[N << 2], *root = &tr[1];

	void build(int p, int l, int r) {
		tr[p] = Node(l, r);
		int mid = (l + r) / 2;
		if (l != r) {
			tr[p].lc = &tr[p * 2];
			tr[p].rc = &tr[p * 2 + 1];
			build(p * 2, l, mid);
			build(p * 2 + 1, mid + 1, r);
		}
	}

	void add(Node *p, int l, int r, int x) {
		p->pushdown();
		if (p->fit(l, r)) {
			p->d += x;
		} else {
			int mid = (p->l + p->r) / 2;
			if (l <= mid) add(p->lc, l, r, x);
			if (r >  mid) add(p->rc, l, r, x);
			p->update();
		}
	}

int a[N], b[N];
int ans[N];
vector  in[N], out[N];

int main() {
	scanf("%d%d", &n, &m);
	for (int x, y, i = 1; i < n; i++) {
		scanf("%d%d", &x, &y);
		E[x].push_back(y);
		E[y].push_back(x);
	}
	dfs(1, 0);
	build(1, 1, n);
	for (int i = 0; i < m; i++) {
		scanf("%d%d", &a[i], &b[i]);
		in[st[a[i]]].push_back(i);
		in[st[b[i]]].push_back(i);
		out[ed[a[i]]].push_back(i);
		out[ed[b[i]]].push_back(i);
	}
	int cnt = 0;
	for (int i = 1; i <= n; i++) {
		vector ::iterator it;
		for (it = in[i].begin(); it != in[i].end(); it++) {
			add(root, st[a[*it]], ed[a[*it]], 1);
			add(root, st[b[*it]], ed[b[*it]], 1);
			cnt++;
		}
		ans[order[i]] = root->minv + root->d ? n - 1 : n - root->num - !!cnt ;
		for (it = out[i].begin(); it != out[i].end(); it++) {
			add(root, st[a[*it]], ed[a[*it]], -1);
			add(root, st[b[*it]], ed[b[*it]], -1);
			cnt--;
		}
	}
	for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
}