zoukankan      html  css  js  c++  java
  • 「HZOJ NOIP2020 Round #13」20201127模拟 题解

    NOIP2020 模拟 - 学军 30

    解题报告

    expect2004


    棒棒糖

    题解

    观察到题中所给的后半部分伪代码,实际是倍增求 (operatorname{LCA})

    观察给出的有 bug 的 dfs 代码,实际上只会影响每个点的 dep。

    倍增求 ( exttt{LCA}) 的部分中,后半部分,即 (x,y) 两个点一起往上跳的部分是正确的,那么只有将 (x,y) 提平的部分可能导致答案错误——如果不能提到同一高度,答案一定错误。

    那么考虑什么情况才能让两个结点提平:(operatorname{dep}(x) - operatorname{dep}(y)) 的值是正确的,这种情况发生且仅发生在从 (operatorname{LCA})(x,y) 路径上,(operatorname{RANDOM}) 函数值为 (0) 的次数一样多。

    (X = operatorname{dep}(x) - operatorname{dep}(operatorname{LCA}), Y = operatorname{dep}(y) - operatorname{dep}(operatorname{LCA}))

    得到答案为 (dfrac{sum_{i=0}^{min{X,Y}}{C_X^i imes C_Y^i}}{sum_{i=0}^X{C_X^i} imessum_{i=0}^Y{C_Y^i}})

    得到上式,即可 (O(n^2)) 预处理,可以获得 65 分

    分子分母分开讨论,分母显然等于 (2^{X+Y})

    通过打表或者考虑组合意义,分子等于 (C_{X+Y}^{min {X, Y}})

    可以 (O(n)) 预处理组合数,(O(Q log n)) 回答询问。

    至此,我们以 (O(Q log n)) 的复杂度解决了本题。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define dbg(x) cerr << #x << " = " << x << endl;
    
    template < typename Tp >
    inline void rd(Tp &x) {
    	x = 0; int fh = 1; char ch = 1;
    	while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    	if(ch == '-') fh = -1, ch = getchar();
    	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    	x *= fh;
    }
    
    const int mod = 998244353;
    template < typename Tp > inline void smax(Tp &x, Tp y) {if(x < y) x = y;}
    template < typename Tp > inline void smin(Tp &x, Tp y) {if(x > y) x = y;}
    template < typename Tp > inline void chkadd(Tp &x, Tp y) {x = (x + y) % mod;}
    template < typename Tp > inline Tp abs(Tp x) {return (x < 0) ? -x : x;}
    
    const int maxn = 200000 + 7;
    const int maxm = 400000 + 7;
    
    int n, Q;
    int Head[maxn], to[maxm], Next[maxm], tot;
    int dep[maxn], fa[maxn], top[maxn], size[maxn], son[maxn];
    void add(int x, int y) {
    	to[++tot] = y, Next[tot] = Head[x], Head[x] = tot;
    }
    
    void dfs1(int x, int f) {
    	dep[x] = dep[f] + 1, size[x] = 1, fa[x] = f;
    	int mx = -1;
    	for(int i = Head[x]; i; i = Next[i]) {
    		int y = to[i];
    		if(y == f) continue;
    		dfs1(y, x); size[x] += size[y];
    		if(size[y] > mx) mx = size[y], son[x] = y;
    	}
    }
    
    void dfs2(int x, int tp) {
    	top[x] = tp;
    	if(!son[x]) return ;
    	dfs2(son[x], tp);
    	for(int i = Head[x]; i; i = Next[i]) {
    		int y = to[i];
    		if(y == son[x] || y == fa[x]) continue;
    		dfs2(y, y);
    	}
    }
    
    inline int LCA(int x, int y) {
    	while(top[x] != top[y]) {
    		if(dep[top[x]] < dep[top[y]]) swap(x, y);
    		x = fa[top[x]];
    	}
    	if(dep[x] < dep[y]) return x;
    	return y;
    }
    
    long long fpow(long long x, int p) {
    	long long res = 1ll;
    	while(p) {
    		if(p & 1) res = res * x % mod; p >>= 1;
    		x = x * x % mod;
    	}
    	return res;
    }
    
    int frac[maxn * 2], inv[maxn * 2];
    
    inline void Preprocess(void) {
    	frac[0] = 1;
    	for(int i = 1; i <= 400000; i++) frac[i] = 1ll * frac[i - 1] * i % mod;
    	inv[400000] = fpow(frac[400000], mod - 2);
    	for(int i = 399999; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
    }
    
    long long C(int x, int y) {
    	if(x < y) return 0;
    	return 1ll * frac[x] * inv[y] % mod * inv[x - y] % mod;
    }
    
    inline void query(int x, int y) {
    	int lca = LCA(x, y);
    	int dpx = dep[x] - dep[lca], dpy = dep[y] -dep[lca];
    	int Mn = min(dpx, dpy);
    	long long ans = C(dpx + dpy, Mn);
    	ans = 1ll * ans * fpow(fpow(2, dpx), mod - 2) % mod;
    	ans = 1ll * ans * fpow(fpow(2, dpy), mod - 2) % mod;
    	printf("%lld
    ", ans);
    }
    
    inline void Init(void) {
    	rd(n);
    	for(int i = 1, x, y; i < n; i++) {
    		rd(x); rd(y);
    		add(x, y); add(y, x);
    	}
    }
    
    inline void Work(void) {
    	Preprocess();
    	dfs1(1, 0);
    	dfs2(1, 1);
    	rd(Q);
    	while(Q--) {
    		int x, y; rd(x); rd(y);
    		query(x, y);
    	}
    }
    
    signed main(void) {
    	Init();
    	Work();
    	return 0;
    }
    

    彩虹糖

    题解

    注意到第三条限制,即是保证 (forall i in [1,m]) ,命题 (i in A) 与命题 (i in B) 不同时成立,因此,这是一个公平游戏。

    显然,由于两人都绝顶聪明,都要最大化两人的可操作次数差,由于上述限制条件,操作顺序并不影响答案。

    (f(i)) 表示利用大小为 (i) 的堆先手能与后手拉开的最大可操作次数差,(-g(i)) 表示利用大小为 (i) 的堆后手能与先手拉开的最大可操作次数差。

    由于上述限制,在处理时可合并处理为 (f(i)) 表示利用大小为 (i) 的堆先手能与后手拉开的最大可操作次数差 后手能与先手拉开的最小可操作次数差,这取决于 (i) 属于哪一个区间。

    由于 (forall x in A cup B,x in [1,m]),任何大于 (m) 的堆数无用。

    排除掉无用堆后,令 (S = sum_{i=1}^n f(P_i)),当 (S>0) 时,答案为 Pomegranate,否则答案为 Orange。

    值得指出的是,Subtask3 满足特殊限制 (tot_b = 0),但这并不表示先手必胜,因为在 (n) 堆中可以没有任何一堆先手能够操作的。我在制作测试数据时,特别手工构造了这样一组数据,并发现有选手因此失分。

    时间复杂度貌似是 (O(dfrac{1}{2}m^2+n)),对于这个数据范围显得有些卡常。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define dbg(x) cerr << #x << " = " << x << endl;
    
    template < typename Tp >
    inline void rd(Tp &x) {
       x = 0; int fh = 1; char ch = 1;
       while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
       if(ch == '-') fh = -1, ch = getchar();
       while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
       x *= fh;
    }
    
    const int mod = 998244353;
    template < typename Tp > inline void smax(Tp &x, Tp y) {if(x < y) x = y;}
    template < typename Tp > inline void smin(Tp &x, Tp y) {if(x > y) x = y;}
    template < typename Tp > inline void chkadd(Tp &x, Tp y) {x = (x + y) % mod;}
    template < typename Tp > inline Tp abs(Tp x) {return (x < 0) ? -x : x;}
    
    const int maxn = 1000000 + 7;
    const int maxm = 10000 + 7;
    
    int n, m, A, B, dp[maxn], cnt;
    bool a[maxm], b[maxm];
    int p[maxn];
    
    inline void Init(void) {
       rd(n); rd(m);
       rd(A);
       for(int i = 1, x; i <= A; i++) {
       	rd(x); a[x] = true;
       }
       rd(B);
       for(int i = 1, x; i <= B; i++) {
       	rd(x); b[x] = true;
       }
       for(int i = 1; i <= n; i++) {
       	rd(p[i]);
       	if(p[i] == 1 || p[i] > m) continue;
       	p[++cnt] = p[i];
       }
       n = cnt;
    }
    
    inline void Work(void) {
       for(int i = 2; i <= m; i++) {
       	if(a[i]) {
       		for(int j = 1; j <= i / 2; j++) smax(dp[i], dp[j] + dp[i - j] + 1);
       	}
       	if(b[i]) {
       		for(int j = 1; j <= i / 2; j++) smin(dp[i], dp[j] + dp[i - j] - 1);
       	}
       }
       int ans = 0;
       for(int i = 1; i <= n; i++) {
       	ans += dp[p[i]];
       }
       if(ans > 0) puts("Pomegranate");
       else puts("Orange");
    }
    
    signed main(void) {
       Init();
       Work();
       return 0;
    }
    

    泡泡糖

    毒瘤题,不会


    字典序

    题解

    ((a_i,b_i)) 视作有向图中的一条边,容易发现,在有解的时候,会构成一个 DAG。如果构不成 DAG,即无解。

    容易发现,这个 DAG 的一个拓扑序即为一组合法的答案。

    但要求字典序最小,考虑我们拓扑排序的过程,使用了一个队列,只需要每次贪心地从这个队列中取出最小的继续排序就行了。

    显然用优先队列替换队列进行维护,时间复杂度 (O(n log n))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define dbg(x) cerr << #x << " = " << x << endl;
    
    template < typename Tp >
    inline void rd(Tp &x) {
    	x = 0; int fh = 1; char ch = 1;
    	while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    	if(ch == '-') fh = -1, ch = getchar();
    	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    	x *= fh;
    }
    
    const int mod = 998244353;
    template < typename Tp > inline void smax(Tp &x, Tp y) {if(x < y) x = y;}
    template < typename Tp > inline void smin(Tp &x, Tp y) {if(x > y) x = y;}
    template < typename Tp > inline void chkadd(Tp &x, Tp y) {x = (x + y) % mod;}
    template < typename Tp > inline Tp abs(Tp x) {return (x < 0) ? -x : x;}
    
    const int maxn = 100000 + 7;
    const int maxm = 100000 + 7;
    
    int n, m;
    priority_queue <int, vector <int>, greater <int> > Q;
    int Head[maxn], to[maxm], Next[maxm], tot;
    void add(int x, int y) {
    	to[++tot] = y, Next[tot] = Head[x], Head[x] = tot;
    }
    int deg[maxn], ans[maxn], cas;
    int p[maxn];
    
    inline void Init(void) {
    	rd(n); rd(m);
    	for(int i = 1, x, y; i <= m; i++) {
    		rd(x); rd(y);
    		add(x, y);
    		deg[y]++;
    	}
    }
    
    bool comp(int i, int j) {
    	return ans[i] < ans[j];
    }
    
    inline void Work(void) {
    	for(int i = 1; i <= n; i++) {
    		if(deg[i] == 0) {
    			Q.push(i);
    		}
    		p[i] = i;
    	}
    	while(Q.size()) {
    		int x = Q.top(); Q.pop();
    		ans[x] = ++cas;
    		for(int i = Head[x]; i; i = Next[i]) {
    			int y = to[i]; --deg[y];
    			if(deg[y] == 0) Q.push(y);
    		}
    	}
    	for(int i = 1; i <= n; i++) {
    		if(deg[i]) {
    			puts("-1"); return ;
    		}
    	}
    	sort(p + 1, p + n + 1, comp);
    	for(int i = 1; i < n; i++) printf("%d ", p[i]);
    	printf("%d
    ", p[n]);
    }
    
    signed main(void) {
    	Init();
    	Work();
    	return 0;
    }
    
  • 相关阅读:
    ICEY修改实现ICEY锁血,修改data文件实现主之名关卡重现,顺便改金币
    gg修改器修改聚爆
    MOUSE_OVER 与 ROLL_OVER
    Tween 没有完毕 就停止
    如何将FLex AIR运行环境与AIR程序一起打包(转http://www.awsws.com/?p=94)
    做项目如做人
    flex 打开外部的swf 并调用其方法
    air 零散知识
    贝塞尔曲线的绘制
    air 读取sqlite的Date类型 解决方案
  • 原文地址:https://www.cnblogs.com/liubainian/p/14050267.html
Copyright © 2011-2022 走看看