• 比赛-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-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-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-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} ight) cdot left( ^{m}_{j} ight) 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
    ", ans);
    	return 0;
    }
    

    C. 导数卷积

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

  • 相关阅读:
    一个小笔记(5):A*算法
    一个小笔记(4):递归下降分析法
    1.3 初步了解信号和槽
    一个小笔记(3):约瑟夫环
    1.2 第一个程序
    requestAnimationFrame
    javascript reg 不加入分组
    正则表达式匹配除单词外的任何字符
    自動化ツール(コード生成、パターン抽出)
    windows常用DLL及作用
  • 原文地址:https://www.cnblogs.com/ghcred/p/9434365.html
Copyright © 2020-2023  润新知