zoukankan      html  css  js  c++  java
  • 「NOIP」 联赛模拟测试44

    long long ago,I remember someone set a flag...

    什么都能做,如果你非得树剖加一个 (log),应该也能过。

    不过倍增显然更好处理。

    看题目有一个特别好的一个点来:每次询问的 (v) 保证是 (u) 的祖先。

    所以预处理一个倍增数组和一个 (val) 数组,(val) 用来存这个点走到根节点需要”努力学习“的次数。

    每次询问查询 (u_i) 之上第一个比 (c_i) 大的点 (x)

    如果 (deep[x] < deep[v]),显然是 (0)

    否则求出 (u)(v) 之间的最大值 (tmp),查询 (v_i) 之上第一个比 (tmp) 大的点 (y),最后的答案是 (val[x] - val[y])

    自我感觉写麻烦了

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 1e5 + 50, INF = 0x3f3f3f3f;
    
    inline int read () {
    	register int x = 0, w = 1;
    	register char ch = getchar ();
    	for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
    	for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
    	return x * w;
    }
    
    inline void write (register int x) {
    	if (x / 10) write (x / 10);
    	putchar (x % 10 + '0');
    }
    
    int n, q;
    int w[maxn], val[maxn];
    
    struct Edge {
    	int to, next;
    } e[maxn << 1];
    
    int tot, head[maxn];
    
    inline void Add (register int u, register int v) {
    	e[++ tot].to = v;
    	e[tot].next = head[u];
    	head[u] = tot;
    }
    
    int deep[maxn];
    int f[maxn][21], maxx[maxn][21];
    
    inline void DFS0 (register int u, register int fa) {
    	deep[u] = deep[fa] + 1, maxx[u][0] = w[u];
    	for (register int i = 1; (1 << i) <= deep[u]; i ++) {
    		f[u][i] = f[f[u][i - 1]][i - 1];
    		maxx[u][i] = max (maxx[u][i - 1], maxx[f[u][i - 1]][i - 1]);
    	}
    	for (register int i = head[u]; i; i = e[i].next) {
    		register int v = e[i].to;
    		if (v == fa) continue;
    		f[v][0] = u, DFS0 (v, u);
    	}
    }
    
    inline int Find (register int u, register int c) {
    	for (register int i = 20; i >= 0; i --) {
    		if (maxx[u][i] <= c) u = f[u][i];
    	}
    	return u;
    }
    
    inline int Getmax (register int u, register int v) {
    	register int maxval = 0;
    	for (register int i = 20; i >= 0; i --) {
    		if (deep[f[u][i]] >= deep[v]) maxval = max (maxval, maxx[u][i]), u = f[u][i];
    	}
    	return max (maxval, w[v]);
    }
    
    inline void DFS1 (register int u, register int fa) {
    	val[u] = val[Find (u, w[u])] + 1;
    	for (register int i = head[u]; i; i = e[i].next) {
    		register int v = e[i].to;
    		if (v == fa) continue;
    		DFS1 (v, u);
    	}
    }
    
    int main () {
    	freopen ("tree.in", "r", stdin);
    	freopen ("tree.out", "w", stdout);
    	n = read(), q = read(), memset (maxx, 0x3f, sizeof maxx);
    	for (register int i = 1; i <= n; i ++) w[i] = read();
    	for (register int i = 1; i <= n - 1; i ++) {
    		register int u = read(), v = read();
    		Add (u, v), Add (v, u);
    	}
    	DFS0 (1, 0), DFS1 (1, 0);
    	while (q --) {
    		register int u = read(), v = read(), c = read();
    		register int x = Find (u, c);
    		if (deep[x] < deep[v]) {
    			puts ("0");
    		} else {
    			register int tmp = Getmax (u, v);
    			register int y = Find (v, tmp);
    			printf ("%d
    ", val[x] - val[y]);
    		}
    	}
    	return 0;
    }
    

    环 circle

    显然我们要求的是最小环的数量,而且因为这些环在一个完全图中,所有的非三元环都会被分解成三元环(画图易证),所以这些环只能是三元环。

    问题转化成了求三元环的期望数。

    如果没有限制,显然答案是从 (n) 个点中选 (3) 个点:

    [inom{n}{3}=frac{n imes (n - 1) imes (n - 2)}{6} ]

    用容斥减去不合法的情况,对于一个三元图有 (8) 种情况:

    容易发现,对于一个不合法的三元环,总是有一个点的出度为 (2)

    可以推得:对于一个三元组 ((a,b,c)),如果 (bin p_a;且;cin p_a;且;b eq c)(p_a) 表示 (a) 出边到达的点的集合),则这个三元组不是三元环。

    考虑对每个点 (i) 减去上面的贡献,设 (a_i) 表示 (i) 确定出边的数量,(b_i) 表示 (i) 除去确定连的边还要连的边,则每次减去的期望是:

    [frac{a_i imes(a_i - 1)}{2}+a_i imes frac{b_i}{2}+frac{b_i imes(b_i - 1)}{2} imes frac{1}{4} ]

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    typedef long long ll;
    
    using namespace std;
    
    const int maxn = 1e5 + 50, INF = 0x3f3f3f3f, mod = 1e9 + 7;
    
    inline int read () {
    	register int x = 0, w = 1;
    	register char ch = getchar ();
    	for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
    	for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
    	return x * w;
    }
    
    inline void write (register int x) {
    	if (x / 10) write (x / 10);
    	putchar (x % 10 + '0');
    }
    
    int n, m, deg[maxn], a[maxn], b[maxn];
    ll ans;
    
    inline ll qpow (register ll a, register ll b) {
    	register ll ans = 1;
    	while (b) {
    		if (b & 1) ans = ans * a % mod;
    		a = a * a % mod, b >>= 1;
    	}
    	return ans;
    }
    
    int main () {	
    	freopen ("circle.in", "r", stdin);
    	freopen ("circle.out", "w", stdout);
    	n = read(), m = read(), ans = 1ll * n * (n - 1) % mod * (n - 2) % mod * qpow (6, mod - 2) % mod;
    	for (register int i = 1; i <= m; i ++) {
    		register int u = read(), v = read(); deg[u] ++, deg[v] ++, a[u] ++;
    	}
    	for (register int i = 1; i <= n; i ++) {
    		b[i] = n - 1 - deg[i];
    		ans = ((ans - 1ll * a[i] * (a[i] - 1) % mod * qpow (2, mod - 2) % mod - 1ll * a[i] * b[i] % mod * qpow (2, mod - 2) % mod - 1ll * b[i] * (b[i] - 1) % mod * qpow (2, mod - 2) % mod * qpow (4, mod - 2) % mod) % mod + mod) % mod;
    	}
    	printf ("%lld
    ", ans);
    	return 0;
    }
    

    礼物 gift

    一个转化式:

    [inom{a_i+a_j+b_i+b_j}{a_i+a_j}=sum^{a_i+a_j}_{t=0}inom{a_i+b_i}{t} imes inom{a_j+b_j}{a_i+a_j-t}=sum^{a_j}_{t=-a_i}inom{a_i+b_i}{a_i+t} imes inom{a_j+b_j}{a_j-t} ]

    原式可以理解为从 ((-a_i,-b_i)) 走到 ((a_j,b_j)) 的方案数。

    然后,按 (y=-x) 将两边分开,可以转化为从 ((-a_i,-b_i)) 横着走 (t) 步到 (y=-x) 上,再横着走 (a_i+a_j-t) 步到 ((a_j,b_j))

    根据式子发现,(t) 只用枚举 (-a_i)(b_i)

    考虑实现:

    我们可以将每个 (i),用某个 (t) 求出前半部分的值,再乘上对应的 (t)(1sim i-1) 的后半部分的值的总和,最后的答案乘上 (2) 即可。

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 1e5 + 50, INF = 0x3f3f3f3f, mod = 1e9 + 7, base = 2e7;
    
    inline int read () {
    	register int x = 0, w = 1;
    	register char ch = getchar ();
    	for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
    	for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
    	return x * w;
    }
    
    inline void write (register int x) {
    	if (x / 10) write (x / 10);
    	putchar (x % 10 + '0');
    }
    
    int n, maxx, ans;
    int a[maxn], b[maxn];
    int fac[20000005], facinv[20000005], val[40000005];
    
    inline int qpow (register int a, register int b) {
    	register int ans = 1;
    	while (b) {
    		if (b & 1) ans = 1ll * ans * a % mod;
    		a = 1ll * a * a % mod, b >>= 1;
    	}
    	return ans;
    }
    
    inline void Init () {
    	fac[0] = 1;
    	for (register int i = 1; i <= maxx; i ++) 
    		fac[i] = 1ll * fac[i - 1] * i % mod;
    	facinv[maxx] = qpow (fac[maxx], mod - 2);
    	for (register int i = maxx; i >= 1; i --) 
    		facinv[i - 1] = 1ll * facinv[i] * i % mod;
    }
    
    inline int C (register int a, register int b) {
    	if (a == 0 || b == 0 || a == b) return 1;
    	if (a < 0 || b < 0 || a < b) return 0;
    	return 1ll * fac[a] * facinv[b] % mod * facinv[a - b] % mod;
    }
    
    int main () {
    	freopen ("gift.in", "r", stdin);
    	freopen ("gift.out", "w", stdout);
    	n = read();
    	for (register int i = 1; i <= n; i ++) 
    		a[i] = read(), b[i] = read(), maxx = max (maxx, a[i] + b[i]);
    	Init ();
    	for (register int i = 1; i <= n; i ++) {
    		for (register int t = -a[i]; t <= b[i]; t ++) 
    			ans = (ans + 1ll * C (a[i] + b[i], a[i] + t) * val[t + base] % mod) % mod;
    		for (register int t = -b[i]; t <= a[i]; t ++) 
    			val[t + base] = (val[t + base] + C (a[i] + b[i], a[i] - t)) % mod;
    	}
    	printf ("%lld
    ", 2ll * ans % mod);
    	return 0;
    }
    

    最优排名

    考虑贪心取,我们取 (v) 比当前的 (v_1) 大的所有元素中 (w_i-v_i) 最小的,每次 (v_1) 减小的时候,用单调指针将后面 (v_i) 比它大的元素放到堆里。

    会发现,我们每次贪心只会造成局部最优,并不能保证全局最优,所以每次操作的时候取个 (min) 即可。

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    
    typedef long long ll;
    
    using namespace std;
    
    const int maxn = 3e5 + 50, INF = 0x3f3f3f3f;
    
    inline ll read () {
    	register ll x = 0, w = 1;
    	register char ch = getchar ();
    	for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
    	for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
    	return x * w;
    }
    
    inline void write (register int x) {
    	if (x / 10) write (x / 10);
    	putchar (x % 10 + '0');
    }
    
    int n, r = 1, ans, num;
    ll s;
    
    struct Node {
    	ll v, w;
    	inline bool operator < (const Node &x) const { return v > x.v; }
    } a[maxn];
    
    priority_queue <ll, vector <ll>, greater <ll> > q;
    
    int main () {
    	freopen ("rank.in", "r", stdin);
    	freopen ("rank.out", "w", stdout);
    	n = read() - 1, s = read(), read();
    	for (register int i = 1; i <= n; i ++) {
    		a[i].v = read(), a[i].w = read();
    		if (a[i].v <= s) ans ++;
    	}
    	sort (a + 1, a + n + 1);
    	for (register int i = 1; i <= n; i ++, r = i) {
    		if (a[i].v <= s) break;
    		q.push (a[i].w - a[i].v + 1);
    	}
    	while (! q.empty ()) {
    		register ll u = q.top ();
    		if (s < u) break;
    		q.pop (), s -= u, num ++;
    		while (a[r].v > s && r <= n) q.push (a[r].w - a[r].v + 1), r ++;
    		ans = max (ans, num + n - r + 1);
    	}
    	printf ("%d
    ", n + 1 - ans);
    	return 0;
    }
    
  • 相关阅读:
    毕业设计(高校网上作业提交系统)开发记录(15)
    毕业设计(高校网上作业提交系统)开发记录(14)
    毕业设计(高校网上作业提交系统)开发记录(13)
    毕业设计(高校网上作业提交系统)开发记录(12)
    毕业设计(高校网上作业提交系统)开发记录(11)
    毕业设计(高校网上作业提交系统)开发记录(10)
    毕业设计(高校网上作业提交系统)开发记录(9)
    毕业设计(高校网上作业提交系统)开发记录(8)
    Java实现沙箱测试环境支付springboot
    Java面试宝典2019
  • 原文地址:https://www.cnblogs.com/Rubyonly233/p/14069537.html
Copyright © 2011-2022 走看看