• ZR国庆Round2解题报告


    心路历程

    预计得分:100 + 10 - 20 + 10 = 120

    实际得分:100 + 0 + 10 = 110

    感觉这场打的挺稳的。开场秒掉A题,写+调差不多1h

    然后刚T3暴力,刚完还有2h左右。。然后,,这时候我zz的选择去打T2的暴力,然而T2暴力真的不是一般的难写。。

    终于又花了1h打完T2暴力,又打了打(n <= 10)的表,感觉稳的一批。

    然后开始跑(n = 11)的,结果到比赛结束也没跑出来qwq。。

    离比赛结束还有5min的时候发现T3跟K无关又少拿了20

    下午看成绩的时候发现T2 (n = 1)的点玩错了完美爆零。。。。。。。

    如果。。。再给我20min让我打完T2的表、、、、

    如果。。。再给我15min让我打完T3的优化版暴力。。。。

    好像就win了啊可惜没如果。。。。。

    T1

    单调队列随便做。。。

    维护最大最小值,贪心的pop一下就好了。。

    因为数据是随机的,所以单调队列里的元素不会很多,deque维护一下。。

    #include<bits/stdc++.h>
    #define LL long long 
    using namespace std;
    const int MAXN = 2e7 + 10;
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    struct Node {
    	int val, pos;
    }; 
    int N, K, seed, l, r; 
    deque<Node> qmx, qmn;
    int main() {
    	N = read(); K = read();
    	seed = read(); l = read(), r = read();
    	LL ans = 0, pre = 0;
    	for(int i = 1; i <= N; i++) {
    		int a = seed % (r - l + 1) + l;
            seed = (13331ll * seed + 23333) % 1000000007;
    		while(!qmx.empty() && a > qmx.back().val) qmx.pop_back();
    		while(!qmn.empty() && a < qmn.back().val) qmn.pop_back();
    		//cout << a << endl;
    		qmx.push_back((Node) {a, i});
    		qmn.push_back((Node) {a, i});	
    		while(!qmx.empty() && !qmn.empty() && (1ll * qmx.front().val > 1ll * qmn.front().val * K) ) {
    			if(qmx.front().pos < qmn.front().pos) pre = qmx.front().pos, qmx.pop_front();
    			else pre = qmn.front().pos, qmn.pop_front();
    		}
    		if(!qmx.empty() && !qmn.empty()) ans += i - pre;
    	}
    	cout << ans;
    	return 0;
    }
    /*
    20000000 6
    1234 4321 8765
    
    */
    

    T2

    这题是真神仙题啊。。。

    直接说标算吧。

    (f[i][j])表示(i)个节点的无根树,最大深度至多为(j)的方案数,

    (g[i][j])表示(i)个节点的无根树,最大最大深度恰好为(j)的方案数,显然(g[i][j] = f[i][j] - f[i][j - 1])

    那么(f[i][j] = i * sum_{k = 0}^i frac{f[i - k][j]}{i - k} * f[k][j - 1] * C_{n - 2}^{k - 1})

    一个公式。。包含了无数个Trick。。Orz xudyh。

    首先把无根树计数变成有根树计数,然后枚举与根节点相连的编号最小的点(k)

    前面要除掉(i - k)的原因是因为此时我们不清楚根节点

    最后(C_{n - 2}^{k - 1})的意思是我们钦定了根节点和它相连的编号最小的点之后的答案

    那么统计答案的时候

    • 如果直径是(2j + 1),那么答案为

    [sum g[k][j] * g[N - k][j] * C_{n - 1}^{k - 1} ]

    这个应该比较好理解,就是左右分别算一算。

    • 如果直径是(2j), 答案为

    (g[N][j] - sum g[k][j - 1] * f[N - k][j - 1] * C_n^k)

    这个就比较有意思了,解释一下

    现在我们钦定了一个根节点,只需要统计以它为中心的答案

    (g[n][j])表示的是深度最大为(j)的方案数,但是这里面会有一些不满足答案(只有一条长为(j)的链)

    考虑减去不满足条件的

    枚举两棵子树,(g[k][j - 1])保证了其中的一个满足条件,另一个不满足条件的方案就是从根节点开始深度至多为(j - 1)的方案

    标算代码看不懂。。。自己写的调不出来。。咕咕咕。。

    #include<bits/stdc++.h>
    #define chmax(a, b) (a = (a < b ? b : a))
    #define chmin(a, b) (a = (a < b ? a : b))
    //#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    using namespace std;
    const int MAXN = 501;
    // char buf[1 << 21], *p1 = buf, *p2 = buf;
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	while(c < '0' || c > '9') {if(c == '-')f =- 1; c = getchar();}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    int N, M, f[MAXN][MAXN], g[MAXN][MAXN], C[MAXN][MAXN], mod;
    int fastpow(int a, int p) {
    	int base = 1;
    	while(p) {
    		if(p & 1) base = 1ll * a * base % mod;
    		a = 1ll * a * a % mod; p >>= 1;
    	}
    	return base;
    }
    int inv(int x) {
    	return fastpow(x, mod - 2);
    }
    main() {
    	N = read(); mod = read();
    	C[1][1] = 1;
    	for(int i = 2; i <= N; i++) {
    		C[i][0] = C[i][i] = 1;
    		for(int j = 1; j <= N; j++) C[i][j] = (C[i - 1][j - 1] + C[i][j - 1]) % mod;
    	}
    	for(int i = 0; i <= N; i++) f[0][i] = g[0][i] = f[1][i] = g[1][i] = 1;
    	for(int i = 1; i <= N; i++) {
    		f[i][1] = i; 
    		for(int j = 2; j <= N; j++) {
    			int sum = 0;
    			for(int k = 0; k <= i; k++) 
    				(sum += 1ll * f[i - k][j] * inv(i - k) % mod * f[k][j - 1] % mod * C[N - 2][k - 1] % mod) %= mod;
    			if(!f[i][j]) f[i][j] = 1ll * i * sum % mod;
    		}
    		for(int j = 0; j <= N; j++) g[i][j] = (f[i][j] - f[i][j - 1] + mod) % mod, printf("%d %d %d
    ", i, j, f[i][j]);
    	}
    	for(int i = 0; i < N; i++) {
    		int ans = 0;
    		if(i & 1) {
    			int j = (i - 1) / 2;
    			for(int k = 1; k < N; k++) (ans += 1ll * g[k][j] * g[N - k][j] % mod * C[N - 1][k - 1] % mod) %= mod;
    			cout << ans << " ";
    		} else {
    			int j = i / 2;
    			ans = g[N][i];
    			for(int k = 1; k < N; k++) ans = (ans - 1ll * g[k][j - 1] * f[N - k][j - 1] % mod * C[N][k] % mod + mod) % mod;
    			cout << ans << " ";
    		}
    	}
    }
    /*
    */	
    

    T3

    首先与(k)无关,因为可以取到(=)

    也就是说每个点只有选或不选两种状态

    直接01分数规划,把每个(a[i] - x),问题转化为能不能删去一些点,使得剩下的权值(>0)

    那么一个点能成为答案,当且仅当它不是孤立点且与其他不选的点形成了独立集(因为两条边之间最小有一个要选)

    也就是说我们要找出(a[i] - val)最小的独立集,

    但是似乎并不好搞,取一下负,找出(val - a[i])最大的独立集

    然后就可以dp了。

    (dp[i])表示考虑右侧的(i)个点,枚举上一个点(j),再枚举一下左边的点。观察能否加入独立集

    第三维前缀和优化一下,时间复杂度:(O(n^2))

    换一种dp方式,加一个(f[j])表示从(j)转移到当前点的最优代价

    for i = 1...n
    	dp[i] = max(f[j], 1 <= j < i)
    	f[i] = dp[i]
    	for all 区间 r = i
     	[l, r] = w
    	f[0....l - 1]  += W
    

    线段树优化一下,interesting。

    复杂度:(O(nlog^2n))

    #include<bits/stdc++.h>
    #define chmax(a, b) (a = (a < b ? b : a))
    #define chmin(a, b) (a = (a < b ? a : b))
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    #define ls k << 1
    #define rs k << 1 | 1 
    using namespace std;
    const int MAXN = 30001;
    const double INF = 1e18, eps = 1e-9;
    char buf[1 << 21], *p1 = buf, *p2 = buf;
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	while(c < '0' || c > '9') {if(c == '-')f =- 1; c = getchar();}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    int N, M, K, L[MAXN], R[MAXN];
    vector<int> v[MAXN];
    double dp[MAXN], a[MAXN], b[MAXN], ta[MAXN], tb[MAXN];
    struct Node {
    	int l, r, siz;
    	double v, f;
    }T[MAXN << 2];
    void add(int k, double val) {
    	T[k].v += val; T[k].f += val;
    }
    void pushdown(int k) {
    	if(T[k].f < eps) return ;
    	add(ls, T[k].f); add(rs, T[k].f);
    	T[k].f = 0;
    }
    void update(int k) {
    	T[k].v = max(T[ls].v, T[rs].v);
    }
    void Build(int k, int ll, int rr) {
    	T[k].l = ll; T[k].r = rr; T[k].siz = rr - ll + 1; T[k].v = T[k].f = 0;
    	if(ll == rr) return ;
    	int mid = T[k].l + T[k].r >> 1;
    	Build(ls, ll, mid); Build(rs, mid + 1, rr);
    	update(k);
    }
    void IntAdd(int k, int ll, int rr, double val) {
    	if(ll <= T[k].l && T[k].r <= rr) {
    		add(k, val); return ;
    	}
    	pushdown(k);
    	int mid = T[k].l + T[k].r >> 1;
    	if(ll <= mid) IntAdd(ls, ll, rr, val); 
    	if(rr >  mid) IntAdd(rs, ll, rr, val);
    	update(k);
    }
    double Query(int k, int ll, int rr) {
    	if(ll <= T[k].l && T[k].r <= rr) return T[k].v;
    	pushdown(k);
    	int mid = T[k].l + T[k].r >> 1;
    	if(ll > mid) return Query(rs, ll, rr);
    	else if(rr <= mid) return Query(ls, ll, rr);
    	else return max(Query(ls, ll, rr), Query(rs, ll, rr));
    }
    bool check(double val) {
    	double sum = 0;
    	for(int i = 1; i <= N; i++) ta[i] = max((double)0, val - a[i]), sum += a[i] - val;
    	for(int i = 1; i <= M; i++) tb[i] = max((double)0, val - b[i]), sum += b[i] - val;
    	Build(1, 0, M);
    	for(int i = 1; i <= M + 1; i++) {
    		dp[i] = Query(1, 0, i - 1) + tb[i];
    		for(int j = 0; j < v[i].size(); j++)
    			IntAdd(1, 0, L[v[i][j]] - 1, ta[v[i][j]]);
    		if(i != M + 1) IntAdd(1, i, i, dp[i]);
    	}
    	double ans = 0;
    	for(int i = 1; i <= M + 1; i++) ans = max(ans, dp[i]);
    	return sum + ans > -eps;
    }
    main() {
    	//freopen("a.in", "r", stdin);
    	N = read(); M = read(); K = read();
    	double l = INF, r = -INF;
    	for(int i = 1; i <= N; i++) a[i] = read(), chmin(l, a[i]), chmax(r, a[i]);
    	for(int i = 1; i <= M; i++) b[i] = read(), chmin(l, b[i]), chmax(r, b[i]);
    	for(int i = 1; i <= N; i++) {
    		L[i] = read(), R[i] = read();
    		v[R[i]].push_back(i);
    	}
    	double ans = -1;
    	while(r - l > eps) {
    		double mid = (l + r) / 2;
    	//	printf("%.10lf
    ", mid);
    		if(check(mid)) l = mid , ans = mid;
    		else r = mid;
    	}
    	printf("%.10lf", ans);
    
    }
    
  • 相关阅读:
    e824. 获得和设置JSplitPane中的子组件
    e827. 设置JSplitPane中分隔物的大小
    e826. 获得和设置JSplitPane分开的位置
    e788. 取消JSpinner的键盘编辑能力
    e790. 设置JSpinner的边框
    e789. 限制用JSpinner实现数字选择的值
    e787. 用JSpinner实现小时选择
    e793. 监听JSpinner数据变化
    e791. 为JSpinner定制编辑器
    e792. 建立一个包括所有数据的SpinnerListModel
  • 原文地址:https://www.cnblogs.com/zwfymqz/p/9743623.html
Copyright © 2020-2023  润新知