比赛-ZR DAY2 (05 Aug, 2018)

A. 占领地区

离线处理。把主对角线与交矩形上方的那条边所在的直线的交点记录下来。这样对于每条副对角线,查询与它相交的主对角线这个问题就变成了一个区间求和问题。前缀和处理一下就可以了。应把副对角线分成两类讨论,因为可以发现在左上和右下的两条对角线“映射”到矩形上方的那条边所在的直线时,可能会重叠。做题的时候意识模糊去排了个序,其实没有必要,直接枚举位置就好了(桶排序的思想)。

#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

const int _N = 110000;

struct data {
	int l, r;
	data(int l = 0, int r = 0):
		l(l), r(r) { }
	bool operator == (const data &tmp) const
	{
		return l == tmp.l && r == tmp.r;
	}
	bool operator < (const data &tmp) const
	{
		return l != tmp.l ? l < tmp.l : r < tmp.r;
	}
};

ll N, M;
int SX[_N*2][2], len[2];
bool A[3100][3100], X[_N*2];
vector<data> Q[2];

void Update(int x, int y)
{
	if (1 <= x && x <= N && 1 <= y && y <= N) A[x][y] = true;
	return;
}

void Fun1()
{
	int i, j, t;
	ll ans = 0;
	for (i = 1; i <= M; ++i) {
		int x, y;
		scanf("%d%d", &x, &y);
		for (t = 1; t <= N; ++t) Update(t, x+y-t), Update(t, -(x-y-t));
	}
	for (i = 1; i <= N; ++i)
		for (j = 1; j <= N; ++j)
			if (A[i][j]) ++ans;
	printf("%lld\n", N*N-ans);
}

void Fun2()
{
	ll a, b, ans = 0;
	scanf("%lld%lld", &a, &b);
	ans += a+b >= N+1 ? N-(a+b-N)+1 : a+b-1;
	ans += b-a >= 0 ? N+(a-b-1)+1 : -(a-b-N);
	printf("%lld\n", N*N-ans+1);
}

void Fun3()
{
	int EX = N+100, i, j, t;
	ll ans = 0;
	for (i = 1; i <= M; ++i) {
		int a, b, g1_x, g1_y, g2_x, g2_y, k;
		scanf("%d%d", &a, &b);
		t = -(a-b-1);
		X[t+EX] = true;
		if (1 <= a+b-1 && a+b-1 <= N) {
			g1_x = a+b-1, g1_y = 1;
			g2_x = 1, g2_y = a+b-1;
			k = 0;
		} else {
			g1_x = N, g1_y = a+b-N;
			g2_x = a+b-N, g2_y = N;
			k = 1;
		}
		Q[k].push_back(data(-(g1_x-g1_y-1), -(g2_x-g2_y-1)));
	}
	for (i = 1; i <= N+EX; ++i) {
		SX[i][i&1] = SX[i-1][i&1]+X[i];
		SX[i][i&1^1] = SX[i-1][i&1^1];
		if (!X[i]) continue;
		t = 1-(i-EX)+1;;
		if (1 <= t && t <= N)
			ans += N-t+1;//-----------
		else if (1 <= i-EX && i-EX <= N)
			ans += N-(i-EX)+1;//--------
	}
	for (i = 0; i <= 1; ++i) {
		sort(Q[i].begin(), Q[i].end());
		len[i] = unique(Q[i].begin(), Q[i].end())-Q[i].begin();
	}
	for (i = 0; i <= 1; ++i) {
		for (j = len[i]-1; j >= 0; --j) {
			data p = Q[i][j];
			ans += p.r;//---------
			t = (p.r+EX)&1;
			ans -= SX[p.r+EX][t]-SX[p.l-1+EX][t];//----------
		}
	}
	printf("%lld\n", N*N-ans);
	return;
}

int main()
{
	scanf("%lld%lld", &N, &M);
	if (M <= 3005 && N <= 3005) { Fun1(); return 0; }
	if (M == 1) { Fun2(); return 0; }
	Fun3();
	return 0;
}

B. 配对

对每条边计算贡献。期望 = 权值 * (有贡献的方案 / 总方案) 。设一条边权 \(w\) , 连接了两个大小分别为 \(x\)\(y\) 的联通块,在大小为 \(x\) 的块中选 \(i\) 个男生 \(j\) 个女生,分析一波可以得出:

\[e = w \cdot \left( ^{m}_{i} \right) \cdot \left( ^{m}_{j} \right) \cdot x^{i+j} \cdot y^{m - i + m - j} \cdot (min(i, m - j) + min(j, m - i)) \]

\(t = i + j\) ,可以把多个期望合在一块儿贡献……各种预处理,时间复杂度降为 \(O(nm)\)

#include <cstdio>
#include <vector>
#include <algorithm>
#include <ctype.h>

using namespace std;

#define SC(a, b) (static_cast<a>(b))

typedef long long ll;

const int _N = 5200;
const ll MOD = 1e9+7;

struct edge {
	int v, w;
	edge(int v = 0, int w = 0):
		v(v), w(w) { }
};

vector<edge> G[_N];
int ans, N, M, Siz[_N], Mon[_N][_N], f[_N], J[_N], inv[_N];

void getnum(int &num)
{
	char tt;
	while (!isdigit(tt = getchar()));
	num = tt-'0';
	while (isdigit(tt = getchar()))
		num = (num<<3)+(num<<1)+tt-'0';
	return;
}

inline int mul(int a, int b) { return SC(int, SC(ll, a)*b%MOD); }

inline int add(int a, int b)
{
	ll tmp = SC(ll, a)+b;
	return SC(int, tmp > MOD ? tmp-MOD : tmp);
}

int mont(int a, int b)
{
	a %= MOD;
	int t = 1;
	while (b) {
		if (b & 1) t = mul(t, a);
		b >>= 1, a = mul(a, a);
	}
	return t;
}

void DFS(int p, int dad)
{
	Siz[p] = 1;
	for (int i = G[p].size()-1; ~i; --i) {
		edge g = G[p][i];
		if (g.v ^ dad) {
			DFS(g.v, p);
			Siz[p] += Siz[g.v];
			for (int j = 0; j <= (M<<1); ++j) {
				int tmp = mul(mul(Mon[Siz[g.v]][j], Mon[N-Siz[g.v]][(M<<1)-j]), f[j]);
				ans = add(ans, mul(tmp, g.w));
			}
		}
	}
	return;
}

inline int GetC(int dn, int up) { return mul(mul(J[dn], inv[dn-up]), inv[up]); }

int main()
{
	int i, j;
	getnum(N), getnum(M);
	for (i = 1; i < N; ++i) {
		int a, b, c;
		getnum(a), getnum(b), getnum(c);
		G[a].push_back(edge(b, c)), G[b].push_back(edge(a, c));
	}
	for (i = 1; i <= N; ++i) {
		Mon[i][0] = 1;
		for (j = 1; j <= (M<<1); ++j)
			Mon[i][j] = mul(Mon[i][j-1], i);
	}
	J[0] = 1;
	for (i = 1; i <= M; ++i) J[i] = mul(J[i-1], i);
	inv[M] = mont(J[M], MOD-2);
	for (i = M-1; i >= 0; --i) inv[i] = mul(inv[i+1], i+1);
	for (i = 0; i <= M; ++i)
		for (j = 0; j <= M; ++j)
			f[i+j] = add(f[i+j], mul(add(min(i, M-j), min(j, M-i)), mul(GetC(M, i), GetC(M, j))));
	DFS(1, -1);
	printf("%d\n", ans);
	return 0;
}

C. 导数卷积

太毒瘤了,不会做。极度痛苦地打完 \(O(n ^ 2 \cdot log\ n)\) 的 NTT 想拿 40 暴力分,结果 ZROJ 太快让 \(O(n ^ 3)\) 的纯暴力也过去了,血亏。然后 10 分的特殊样例没来得及看 Orz 。正解不太懂……

posted @ 2018-08-07 00:20  derchg  阅读(113)  评论(0编辑  收藏  举报