zoukankan      html  css  js  c++  java
  • 2018.08.23高二互测

    2018.08.24 NOIp模拟赛

    今天是doe的神题,表示根本做不出,还好可以(~IOI~)实时评测,不然第一题都被(~Subtask~)捆死了。。。

    第一题

    第一眼看着题感觉把所有的叶子节点都染黑很优秀,但看样例就知道是错的。于是发现对于一个点,在他的儿子节点中,只要选得只剩下一个就是正确并且优秀的。而当前点被选之后,就把他标记一下不计入他父亲节点那一层的答案统计。但是这个DP会根据树的形态而改变,比如说对于一条链,如果选取两端作为根节点则答案为(~0),但答案应为(~1),所以挑一个好根很重要。再发现,若根的度大于(~2)时,其连向祖先的联通块中必然有点为⿊点,所以就选一个度大于等于(~3)的就好了。

    code

    #include<bits/stdc++.h>
    #define For(i, j, k) for(int i = j; i <= k; ++i)
    #define Forr(i, j, k) for(int i = j; i >= k; --i)
    #define Travel(i, u) for(int i = beg[u], v = to[i]; i; i = nex[i], v = to[i])
    using namespace std;
    
    inline int read() {
    	int x = 0, p = 1; char c = getchar();
    	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
    	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x *= p;
    }
    
    inline void File() {
    	freopen("bf.in", "r", stdin);
    	freopen("bf.out", "w", stdout);
    }
    
    const int N = 1e5 + 10;
    int n, e = 1, beg[N], nex[N << 1], to[N << 1];
    int siz[N], u, v, deg[N], rt, ans, dp[N], maxx = 0;
    
    inline void add(int x, int y) {
    	to[++ e] = y, nex[e] = beg[x], beg[x] = e, ++ deg[x];
    	to[++ e] = x, nex[e] = beg[y], beg[y] = e, ++ deg[y];
    }
    
    inline void dfs(int u, int f) {
    	siz[u] = 0;
    	Travel(i, u) if (v != f) {
    		dfs(v, u);
    		int sz = 0, tot = 0;
    		for (int t = beg[v]; t; t = nex[t]) if (to[t] != u) ++ tot, sz += siz[to[t]] == 0;
    		ans += max(0, sz - 1);
    		siz[v] = tot > 1 && tot == sz || tot != sz;
    	}
    }
    
    int main() {
    	File();
    	n = read();
    	For(i, 2, n) u = read(), v = read(), add(v, u);
    	For(i, 0, n - 1) if (deg[i] > maxx) rt = i, maxx = deg[i]; 
    	dfs(rt, -1);
    
    	int sz = 0;
    	Travel(i, rt) if (!siz[v]) ++ sz;
    	ans += max(0, sz - 1);
    	
    	cout << ans << endl;
    	return 0;
    }
    
    

    第二题

    ​ 这是一道很思维的题啊,离散化,首先对错位的数向应在的位置连一条边,可以发现最后的图是一些互不相交的简单环。当几个环中有相同的点权时,可以通过断开再连接的方式把这几个环合并成一个环以减少环数。对于若干个环,先把这几个环首尾连接变成一个大环进行操作,可以发现这样之后只有每个小环的末尾位置没有复位,再对这些数连成一个环。那么对于若干个环最少只需要两次操作就好了。但是有一个(~S~)的限制,由于我很懒就直接搬(~Solution~)了。

    code

    #include<bits/stdc++.h>
    #define For(i, j, k) for(int i = j; i <= k; ++i)
    #define Forr(i, j, k) for(int i = j; i >= k; --i)
    #define Travel(i, u) for(int i = beg[u], v = to[i]; i; v = to[i = nex[i]])
    using namespace std;
    
    inline int read() {
    	int x = 0, p = 1; char c = getchar();
    	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
    	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x *= p;
    }
    
    inline void File() {
    	freopen("gen.in", "r", stdin);
    	freopen("gen.out", "w", stdout);
    }
    
    const int N = 2e5 + 10;
    int n, s, fa[N], e = 1, beg[N], to[N << 1], nex[N << 1], num[N << 1];
    int c[N], tot, a[N], ls[N], vis[N], cnt, k;
    int lst[N], c1, c2;
    
    vector <int> ans[N];
    
    inline void add(int x, int y, int z) {
    	to[++ e] = y, nex[e] = beg[x], beg[x] = e, num[e] = z;
    }
    
    inline void dfs(int u) {
    	vis[u] = cnt;
    	while (beg[u]) {
    		int now = beg[u]; beg[u] = nex[beg[u]];
    		dfs(to[now]), ans[cnt].push_back(num[now]);
    	}
    }
    
    int main() {
    	File();
    	n = read(), s = read();
    	For(i, 1, n) a[i] = ls[i] = read(), fa[i] = i;;
    	sort(ls + 1, ls + 1 + n);
    	For(i, 1, n) c[i] = ls[i] == ls[i - 1] ? c[i - 1] : c[i - 1] + 1;
    
    	tot = unique(ls + 1, ls + 1 + n) - ls - 1;
    	For(i, 1, n) {
    		a[i] = lower_bound(ls + 1, ls + 1 + tot, a[i]) - ls;
    		if (a[i] != c[i]) ++ k, add(a[i], c[i], i);		
    	}
    
    	if (k > s) return puts("-1"), 0;
    	For(i, 1, n) if (!vis[i] && beg[i]) ++ cnt, dfs(i); 	
    
    	if (cnt <= 1 || s - k <= 1) {
    		printf("%d
    ", cnt);
    		For(i, 1, cnt) {
    			printf("%d
    ", ans[i].size());
    			for (auto v : ans[i]) printf("%d ", v); puts("");
    		}
    		return 0;
    	}
    
    	cout << cnt - min(cnt, s - k) + 2 << endl;
    
    	For(i, s - k + 1, cnt) {
    		printf("%d
    ", ans[i].size());
    		for (auto v : ans[i]) printf("%d ", v); puts("");
    	}
    
    	if (s - k > 0) {
    		Forr(i, min(cnt, s - k), 1) {
    			c1 += ans[i].size();
    			lst[++ c2] = ans[i][0];
    		}			
    		
    		printf("%d
    ", c1); 
    		For(i, 1, min(cnt, s - k)) for (auto v : ans[i]) printf("%d ", v); puts("");
    		printf("%d
    ", c2);
    		For(i, 1, c2) printf("%d ", lst[i]); puts("");
    	}
    	return 0;
    }
    
    

    第三题

    ​ 给定⼀棵(~n~)个点树和树上的(~m~)条路径( 互不相同),求这(~m~)条路径中任选两条时其中⼀条完全包含另⼀
    条的概率是多少。

    ​ 只要画个图就很容易推出满足题意的两种情况(一对子树限制和两对区间限制),就不再赘述。考虑用主席树维护(~dfn~),对于每一条路径,在主席树中插入其另一端点的(~dfn~),在查询的时候,由于一棵子树的(~dfn~)是连续的,所以只要查询以在当前这个区间中的(~dfn~)为目标区间的节点个数。但一条路径的端点可以互逆,所以记得反过来查询一下。

    code

    #include<bits/stdc++.h>
    #define For(i, j, k) for(register int i = j; i <= k; ++i)
    #define Forr(i, j, k) for(register int i = j; i >= k; --i)
    #define Travel(i, u) for(register int i = beg[u], v = to[i]; i; i = nex[i], v = to[i])
    using namespace std;
    
    inline int read() {
    	int x = 0, p = 1; char c = getchar();
    	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
    	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x *= p;
    }
    
    inline void File() {
    	freopen("std.in", "r", stdin);
    	freopen("std.out", "w", stdout);
    }
    
    typedef long long ll;
    const int N = 2e5 + 10, M = N << 1;
    int e = 1, beg[N], nex[M], to[M];
    int dep[N], tot, pos[N], fa[18][N];
    int n, m, u, v, st[N], ed[N], rt[N];
    struct node { int x, y; } P[N];
    ll ansx, ansy;
    
    inline void add(int x, int y) {
    	to[++ e] = y, nex[e] = beg[x], beg[x] = e;
    	to[++ e] = x, nex[e] = beg[y], beg[y] = e;
    }
    
    inline void dfs(int u, int f) {
    	pos[st[u] = ++ tot] = u, dep[u] = dep[f] + 1;	
    	fa[0][u] = f;
    	Travel(i, u) if (v != f) dfs(v, u);
    	ed[u] = tot;
    }
    
    namespace PRE {
    #define mid (l + r >> 1)
    	struct node { int l, r, v; } tr[N * 30];
    	int cnt = 0;
    
    	inline void update(int &now, int pre, int l, int r, int x) {
    		now = ++ cnt, tr[now] = tr[pre], ++ tr[now].v;					
    		if (l < r) {
    			if (x <= mid) update(tr[now].l, tr[pre].l, l, mid, x);
    			else update(tr[now].r, tr[pre].r, mid + 1, r, x);
    		}
    	}
    
    	inline int query(int u, int v, int l, int r, int L, int R) {
    		if (tr[v].v - tr[u].v == 0) return 0;
    		if (L <= l && r <= R) return tr[v].v - tr[u].v;
    		if (R <= mid) return query(tr[u].l, tr[v].l, l, mid, L, R);
    		if (L > mid) return query(tr[u].r, tr[v].r, mid + 1, r, L, R);
    		return query(tr[u].l, tr[v].l, l, mid, L, R) + query(tr[u].r, tr[v].r, mid + 1, r, L, R);
    	}
    
    #undef mid
    };
    
    vector <int> vec[N];
    
    int main() {
    	File(), read();
    	using namespace PRE;
    
    	n = read(), m = read();
    	For(i, 2, n) u = read(), v = read(), add(u, v);
    	dfs(1, 0);
    
    	For(j, 1, 17) For(i, 1, n) fa[j][i] = fa[j - 1][fa[j - 1][i]];
    	For(i, 1, m) P[i].x = read(), P[i].y = read(), vec[P[i].x].push_back(P[i].y);
    	
    	For(i, 1, n) { // this if dfn
    		int v = pos[i];
    		if (vec[v].size() == 0) rt[i] = rt[i - 1];
    		else {
    			update(rt[i], rt[i - 1], 1, n, st[vec[v][0]]);
    			For(j, 1, vec[v].size() - 1) update(rt[i], rt[i], 1, n, st[vec[v][j]]);
    		}
    	}
    
    	For(i, 1, m) {
    		int x = P[i].x, y = P[i].y;
    		if (dep[x] < dep[y]) swap(x, y);
    
    		if (st[y] <= st[x] && st[x] <= ed[y]) {
    			int t = x; Forr(i, 17, 0) if (dep[fa[i][t]] > dep[y]) t = fa[i][t];	
    			
    			ansx += query(rt[0], rt[st[t] - 1], 1, n, st[x], ed[x]);	
    			ansx += query(rt[st[x] - 1], rt[ed[x]], 1, n, st[1], st[t] - 1);
    			ansx += query(rt[ed[t]], rt[n], 1, n, st[x], ed[x]);
    			ansx += query(rt[st[x] - 1], rt[ed[x]], 1, n, ed[t] + 1, n);
    
    		} else {
    
    			ansx += query(rt[st[x] - 1], rt[ed[x]], 1, n, st[y], ed[y]);
    			ansx += query(rt[st[y] - 1], rt[ed[y]], 1, n, st[x], ed[x]);
    		}
    		-- ansx;
    	}
    
    	ansy = 1ll * m * (m - 1) / 2;
    	ll gg = __gcd(ansx, ansy); ansx /= gg, ansy /= gg;
    	printf("%lld/%lld
    ", ansx, ansy);
    
    	cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
    	return 0;
    }
    
    
  • 相关阅读:
    http协议学习系列
    git常用命令大全
    git常用命令与常见面试题总结
    MyBatis框架及原理分析
    Mybatis常见面试题总结
    java实现克隆的三种(很最全面)
    java中equals和==之间的区别?clone方法的作用,及其为什么要使用clone方法?如何使用clone复制对象?以及深克隆浅克隆
    ThreadLocal的简单使用及实现的原理
    Java 最常见的 208 道面试题
    TCP流量控制
  • 原文地址:https://www.cnblogs.com/LSTete/p/9532215.html
Copyright © 2011-2022 走看看