zoukankan      html  css  js  c++  java
  • 「MCOI-02」MCOI Round 2 题解

    这场134过于板+水,但是2还是有质量的。所以讲题的顺序也按1342

    「MCOI-02」Convex Hull 凸包

    的确签到,按莫比乌斯反演的套路爆推式子就行了。不妨设 (nle m)

    (ans=sum_{k=1}^{n}d(k)sum_{l|k}mu(l)sum_{k|i}d(i)sum_{k|j}d(j)=sum_{T=1}^{n}sum_{i=1}^{[n/T]}d(i)sum_{j=1}^{[m/T]}d(j)sum_{k|T}d(k)mu(frac{T}{k}))。然后后面那个东西线性筛+埃氏筛一下就好了。其实后面那玩意衡等于 (1) 我却没有发现。。。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN = 2000010;
    template <typename T> void read(T &x) {
    	T f = 1;
    	char ch = getchar();
    	for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    	for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
    	x *= f;
    }
    ll prime[MAXN], mu[MAXN], d[MAXN], mi[MAXN], g[2][MAXN], h[MAXN], cnt;
    bool mark[MAXN]; 
    ll n, m, p, ans;
    void sieve() {
    	mu[1] = d[1] = 1;
    	for (int i = 2; i <= m; i++) {
    		if (!mark[i]) {
    			prime[++cnt] = i;
    			mu[i] = -1;
    			d[i] = 2;
    			mi[i] = 1;
    		}
    		for (int j = 1; j <= cnt && prime[j] * i <= m; j++) {
    			mark[i * prime[j]] = 1;
    			if (i % prime[j] == 0) {
    				d[i * prime[j]] = d[i] / (mi[i] + 1) * (mi[i] + 2);
    				mi[i * prime[j]] = mi[i] + 1;
    				break;
    			}
    			mu[i * prime[j]] = -mu[i];
    			d[i * prime[j]] = d[i] << 1;
    			mi[i * prime[j]] = 1;
    		}
    	}
    	for (int i = 1; i <= m; i++) {
    		for (int j = i; j <= m; j += i) {
    			if (i <= n && j <= n) {
    				g[0][i] += d[j];
    			}
    			g[1][i] += d[j];
    		}
    	}
    	for (int i = 1; i <= m; i++) {
    		for (int j = i; j <= m; j += i) {
    			h[j] += d[i] * mu[j / i];
    			h[j] %= p;
    		}
    	}
    }
    int main() {
    	read(n); read(m); read(p);
    	if (n > m) swap(n, m);
    	sieve();
    	for (int i = 1; i <= n; i++) {
    		ans = (ans + g[0][i] * g[1][i] % p * h[i] % p + p) % p;
    	}
    	printf("%lld", ans);
    	return 0;
    }
    

    「MCOI-02」Ancestor 先辈

    容易发现其实就是判断一个区间是不是单调上升的,只需线段树维护即可。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll MAXN = 1000010;
    const ll inf = 0x7f7f7f7f7f7f7f7f;
    template <typename T> void read(T &x) {
    	T f = 1;
    	char ch = getchar();
    	for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    	for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
    	x *= f;
    }
    struct node{
    	bool mark;
    	ll mi, mx, tag;
    }tr[MAXN << 2];
    ll n, m;
    ll a[MAXN];
    void add(ll x, ll v) {
    	tr[x].mi += v;
    	tr[x].mx += v;
    	tr[x].tag += v;
    }
    void push_down(ll x) {
    	if (tr[x].tag) {
    		add(x << 1, tr[x].tag);
    		add(x << 1 | 1, tr[x].tag);
    		tr[x].tag = 0;
    	}
    }
    void push_up(ll x) {
    	tr[x].mark = (tr[x << 1].mark && tr[x << 1 | 1].mark && tr[x << 1].mx <= tr[x << 1 | 1].mi);
    	tr[x].mi = min(tr[x << 1].mi, tr[x << 1 | 1].mi);
    	tr[x].mx = max(tr[x << 1].mx, tr[x << 1 | 1].mx);
    }
    void build(ll x, ll l, ll r) {
    	if (l == r) {
    		tr[x].mi = tr[x].mx = a[l];
    		tr[x].mark = true;
    		return;
    	}
    	ll mid = (l + r) >> 1;
    	build(x << 1, l, mid);
    	build(x << 1 | 1, mid + 1, r);
    	push_up(x);
    }
    void change(ll x, ll l, ll r, ll L, ll R, ll v) {
    	if (l > R || r < L) return;
    	if (L <= l && r <= R) {
    		add(x, v);
    		return;
    	}
    	ll mid = (l + r) >> 1;
    	push_down(x);
    	change(x << 1, l, mid, L, R, v);
    	change(x << 1 | 1, mid + 1, r, L, R, v);
    	push_up(x);
    }
    bool ans;
    pair<ll, ll> ask(ll x, ll l, ll r, ll L, ll R) {
    	if (!ans) return make_pair(0, 0);
    	if (l > R || r < L) {
    		return make_pair(inf, -inf);
    	}
    	if (L <= l && r <= R) {
    		if (!tr[x].mark) ans = false;
    		return make_pair(tr[x].mi, tr[x].mx);
    	}
    	ll mid = (l + r) >> 1;
    	push_down(x);
    	pair<ll, ll> p = ask(x << 1, l, mid, L, R), q = ask(x << 1 | 1, mid + 1, r, L, R);
    	if (p.second > q.first) ans = false;
    	return make_pair(p.first, q.second);
    }
    int main() {
    	read(n); read(m);
    	for (ll i = 1; i <= n; i++) read(a[i]);
    	build(1, 1, n);
    	for (ll i = 1; i <= m; i++) {
    		ll opt, l, r, x;
    		read(opt); read(l); read(r);
    		if (opt == 1) {
    			read(x);
    			change(1, 1, n, l, r, x);
    		} else {
    			ans = true;
    			pair<ll, ll> tmp = ask(1, 1, n, l, r);
    			if (ans) puts("Yes");
    			else puts("No");
    		}
    	}
    	return 0;
    }
    

    「MCOI-02」Glass 玻璃

    看到这种有线性结构的式子考虑枚举每条边的贡献。考虑以每个点为根,计算与其相连的边的贡献。我们把一条边拆成两部分计算答案,每次计算只用计算子树内的点权给的贡献,显然就是 (sum_{vin subtree(u)}sz[v]*V[v])。然后我们考虑每个点作为根的情况,直接换根dp即可。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll MAXN = 2000010;
    const ll mod = 998244353;
    template <typename T> void read(T &x) {
    	T f = 1;
    	char ch = getchar();
    	for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    	for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
    	x *= f;
    }
    struct node{
    	ll pre, to;
    	ll len;
    }edge[MAXN << 1];
    ll head[MAXN], tot;
    ll n;
    ll val[MAXN];
    ll sz[MAXN], dp[MAXN];
    ll ans;
    void add(ll u, ll v, ll l) {
    	edge[++tot] = node{head[u], v, l};
    	head[u] = tot;
    }
    void dfs1(ll x, ll fa) {
    	sz[x] = 1;
    	for (ll i = head[x]; i; i = edge[i].pre) {
    		ll y = edge[i].to;
    		if (y == fa) continue;
    		dfs1(y, x);
    		sz[x] += sz[y];
    		dp[x] = (dp[x] + dp[y]) % mod;
    	}
    	dp[x] = (dp[x] + sz[x] * val[x]) % mod;
    }
    void get_ans(ll x) {
    	for (ll i = head[x]; i; i = edge[i].pre) {
    		ll y = edge[i].to;
    		ans = (ans + dp[y] * (n - sz[y]) % mod * edge[i].len % mod + mod) % mod;
    	}
    }
    void change_root(ll x, ll y) {
    	//cut
    	dp[x] = (dp[x] - dp[y] + mod) % mod;
    	dp[x] = (dp[x] - sz[x] * val[x] + mod) % mod;
    	sz[x] -= sz[y];
    	dp[x] = (dp[x] + sz[x] * val[x] + mod) % mod;
    	//link
    	dp[y] = (dp[y] + dp[x] + mod) % mod;
    	dp[y] = (dp[y] - sz[y] * val[y] + mod) % mod;
    	sz[y] += sz[x];
    	dp[y] = (dp[y] + sz[y] * val[y]) % mod;
    }
    void dfs2(ll x, ll fa) {
    	get_ans(x);
    	for (ll i = head[x]; i; i = edge[i].pre) {
    		ll y = edge[i].to;
    		if (y == fa) continue;
    		change_root(x, y);
    		dfs2(y, x);
    		change_root(y, x);
    	}
    }
    int main() {
    	read(n);
    	for (ll i = 1; i <= n; i++) read(val[i]);
    	for (ll i = 1, u, v, l; i < n; i++) {
    		read(u); read(v); read(l);
    		add(u, v, l);
    		add(v, u, l);
    	}
    	dfs1(1, 0);
    	dfs2(1, 0);
    	printf("%lld
    ", (ans << 1) % mod);
    	return 0;
    }
    

    「MCOI-02」Build Battle

    首先容易得到一个dp,设 (f_i) 代表匹配到颜色序列的第 (i) 位的方案数,我们只用考虑前 (m) 个位置,设 (s_i)(f_i) 的前缀和,有 (s_i=2*s_{i-1}-s_{i-m-1})

    然后有一个经典trick,我们尝试递归递推式,从 (0) 走到 (n),一开始有一个值 (v=1),有两种走法,一种走到 (i+1),令 (v=v*2),还有一种走到 (i+m+1),令 (v=-v)。然后对于每条到 (n) 的路径,我们记录它的权值和。于是我们发现答案之和行动方式有关,这就变成了一个计数问题。

    [ans=sum_{i=0}^{lfloorfrac{n}{m+1} floor}2^{n-(m+1)*i}*(-1)^i{n-(m+1)*i+ichoose i} ]

    然后就可以 (O(nln n)) 预处理了。

    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    namespace StandardIO{
    	template <typename T> void read(T &x) {
    		T f = 1;
    		char ch = getchar();
    		for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    		for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
    		x *= f;
    	}
    	template <typename T> void write(T x) {
    		if (x > 9) write(x / 10);
    		putchar(x % 10 + '0');
    	}
    	template <typename T> void print(T x) {
    		if (x < 0) putchar('-'), x = -x;
    		write(x);
    		putchar('
    ');
    	}
    }
    
    namespace Solve{
    	const int mod = 1e9 + 7;
    	const int MAXN = 2e6 + 10;
    	const int MAXQ = 1e6 + 10; 
    	static int n, q, m;
    	static long long ans[MAXN];
    	static long long fac[MAXN], ifac[MAXN], inv[MAXN];
    	
    	long long ksm(long long x, long long y) {
    		long long ret = 1;
    		while (y) {
    			if (y & 1) ret = (ret * x) % mod;
    			x = (x * x) % mod;
    			y >>= 1;
    		}
    		return ret;
    	}
    	
    	void init() {
    		fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
    		inv[1] = 1;
    		for (int i = 2; i <= n; i++) {
    			inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    			fac[i] = fac[i - 1] * i % mod;
    			ifac[i] = ifac[i - 1] * inv[i] % mod;
    		}
    	}
    	
    	long long C(long a, long b) {
    		if (a < 0 || b < 0 || a < b) return 0;
    		return fac[a] * ifac[a - b] % mod * ifac[b] % mod;
    	}
    	
    	void MAIN() {
    		StandardIO :: read(n); StandardIO :: read(q);
    		init();
    		while (q--) {
    			StandardIO :: read(m);
    			if (ans[m]) {
    				StandardIO :: print(ans[m]);
    			} else {
    				int i = m;
    				for (int j = 0; j <= n / (i + 1); j++) {
    					ans[i] = (ans[i] + ksm(2, n - (i + 1) * j) * ksm(-1, j) * C(n - i * j, j) % mod + mod) % mod;
    				}
    				StandardIO :: print(ans[m]);
    			}
    		}
    	}
    }
    
    int main() {
    	Solve :: MAIN();
    	return 0;
    }
    
  • 相关阅读:
    (转)CortexM3 (NXP LPC1788)之IIS控制器
    (转)ARMThumb 过程调用标准
    (转)CortexM3 (NXP LPC1788)之看门狗定时器对Flash编程的影响
    (转)CortexM3 (NXP LPC1788)之ADC数模转换器的应用
    (笔记)电路设计(一)之上拉电阻与下拉电阻的应用
    (转)CortexM3 (NXP LPC1788)之EEPROM存储器
    (转)CortexM3 (NXP LPC1788)之IIS应用UDA1380进行音频数据播放
    (转)CortexM3 (NXP LPC1788)之WDT窗口看门狗定时器
    (转)CortexM3 (NXP LPC1788)之SDRAM操作
    (转)CortexM3 (NXP LPC1788)之IIC应用PCA9532进行IO扩展和LED亮度控制
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/13707592.html
Copyright © 2011-2022 走看看