码迷,mamicode.com
首页 > 其他好文 > 详细

【考试总结】小奇模拟赛

时间:2020-04-20 21:41:53      阅读:62      评论:0      收藏:0      [点我收藏+]

标签:直接   维护   push   当当   类型   双向   getch   之间   ret   

\(T1\)

题目描述

给定一棵 \(n\) 个节点的树,求前 \(m\) 条最长路径的长度。

数据范围

序号 \(n\) \(m\) 数据类型
1 10 3 暴力
2 233 23333 暴力
3 2000 300000 暴力
4 2000 300000 暴力
5 50000 1 随机生成
6 7798 17798 随机生成
7 7798 27798 随机生成
8 7798 37798 随机生成
9 50000 300000 1-n 顺序输入的链
10 50000 300000 以 1 为根的菊花图

\(Solution\)

估分:\(70\)

实测:\(40\)

因为写链时脑子抽了居然用了 \(map\) 然后空间炸了,还有判断是否是链那里写错了一点导致第一个点还挂了。。。

对于链:先把 \(1-i(i <= n)\) 的所有链加入堆,然后取出最大的输出,再把这条链删掉上面那条边,扔进堆里

对于菊花图:把每条边排个序,可以构造一个矩形,矩形的 \((x, y)\) 代表的是 \(d[x] + d[y]\)\(d[i]\)\(i\)\(1\) 的边权,先把第一行全部扔进堆里,取最大的输出,再把 \((x + 1, y)\) 上的值扔进去

对于暴力:枚举每条链,用 \(LCA\) 维护链的长度

正解:点分治,把每棵子树做超级钢琴的做法,用 \(st\) 表或者线段树维护

\(Code\)

不知道如何下手...照着网上的一篇题解打的

#include<bits/stdc++.h>
#define F(i, x, y) for(int i = x; i <= y; ++i)
using namespace std;
int read();
const int N = 5e4 + 5;
int n, m;
int sum, root, mx, id, l, r;
int head[N], cnt, ver[N << 1], edge[N << 1], nxt[N << 1];
int st[N * 20][21], d[N * 20], size[N], vis[N], dis[N];
struct node{
    int l, r;
}p[N * 20];
struct use{
    int l, r, h, mq;
};
priority_queue<use> q;
bool operator<(use a,use b)
{
    return d[a.h] + d[a.mq] < d[b.h] + d[b.mq];
}
void add(int x, int y, int z)
{
	ver[++ cnt] = y, edge[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;
}
void get_root(int x, int fa)
{
	size[x] = 1; int maxn = 0;
	for(int i = head[x]; i; i = nxt[i])
	{
		int y = ver[i];
		if(vis[y] || y == fa) continue;
		get_root(y, x), size[x] += size[y], maxn = max(maxn, size[y]);
	}
	maxn = max(maxn, sum - size[x]);
	if(maxn < mx) mx = maxn, root = x;
}
void dfs(int x, int fa)
{
	d[++ id] = dis[x], p[id] = {l, r};
	for(int i = head[x]; i; i = nxt[i])
	    if(! vis[ver[i]] && ver[i] != fa)
	    	dis[ver[i]] = dis[x] + edge[i], dfs(ver[i], x);
}
void solve(int x)
{
	vis[x] = 1, d[++ id] = 0, p[id] = {0, 0}, l = r = id;
	for(int i = head[x]; i; i = nxt[i])
	    if(! vis[ver[i]])
	    	dis[ver[i]] = edge[i], dfs(ver[i], x), r = id;
	for(int i = head[x]; i; i = nxt[i])
	    if(! vis[ver[i]])
	        root = 0, sum = size[ver[i]], mx = INT_MAX, get_root(ver[i], 0), solve(root);
}
int query(int l, int r)
{
	int k = log2(r - l + 1);
	return d[st[l][k]] > d[st[r - (1 << k) + 1][k]] ? st[l][k] : st[r - (1 << k) + 1][k];
}
void pre()
{
	F(i, 1, id) st[i][0] = i;
	F(j, 1, 20)
        F(i, 1, id - (1 << j) + 1)
            if(d[st[i][j - 1]] > d[st[i + (1 << j - 1)][j - 1]]) st[i][j] = st[i][j - 1];
            else st[i][j] = st[i + (1 << j - 1)][j - 1];
    F(i, 1, id)
        if(p[i].l) q.push(use{p[i].l, p[i].r, i, query(p[i].l, p[i].r)});
}
int main()
{
    n = read(), m = read();
    for(int i = 1, x, y, z; i < n; ++ i) x = read(), y = read(), z = read(), add(x, y, z), add(y, x, z);
    sum = n, mx = INT_MAX, get_root(1, 0), solve(root), pre();
    F(i, 1, m)
    {
    	use k = q.top(); q.pop(), printf("%d\n", d[k.mq] + d[k.h]);
    	if(k.mq - k.l) q.push(use{k.l, k.mq - 1, k.h, query(k.l, k.mq - 1)});
    	if(k.r - k.mq) q.push(use{k.mq + 1, k.r, k.h, query(k.mq + 1, k.r)});
	}
	return 0;
}
int read()
{
	int x = 0, f = 1;
	char c = getchar();
	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;
}

\(T3\)

题目描述

星球上有 \(n\) 个城市,标号为 \(1-n\),用 \(n-1\) 条双向通道连接,保证任意两个城市能互相到达。
生化危机爆发了!但由于政府安全能力有限,安全区只包括在标号 \(l\)\(r\) 的城市,你现在在城市 \(x\),想知道最近的安全城市的距离。

数据范围

对于 \(30\%\) 的数据, \(n,q<=1000\)
另有 \(20\%\) 的数据,保证第城市 \(2-n\) 均可直接到达城市 \(1\)
另有 \(10\%\) 的数据,保证城市 i(1<=i<=n-1)可直接到达城市 \(i+1\)
对于 \(100\%\) 的数据, \(n,q<=100000\)\(li<=ri\),任意两个城市的距离小于 \(10^9\)

\(Solution\)

估分:\(60\)

实测:\(60\)

对于链:如果在 \([l \ ,\ r]\) 里输出 \(0\),不在则取 \(min(d[x][l] \ , \ d[x][r])\)\(d[i][j]\) 表示 \(i \ , \ j\) 之间距离

对于菊花图:用 \(st\) 表维护 \(l-r\) 的每个点到 \(1\) 的路径的 \(min\) 值,分情况讨论即可

对于其他:直接 \(dfs\) ,加个剪枝,当当前距离大于 \(ans\) 时返回,可以水过此题

正解:要用到点分治还是点分树来着,我分不太清这两个,大概就是,拆成很多个子树,在每个子树上都做一个动态加点线段树,维护此子树里在 \(l-r\) 中的点到子树根的最小值,用此最小值加上子树根到 \(x\) 的值,更新 \(ans\),然后再往上跳,找新的子树根

\(Code\)

(正解只存在于口胡233

#include<bits/stdc++.h>
#define ll long long
#define F(i, x, y) for(int i = x; i <= y; ++i)
using namespace std;
ll read();
const int N = 1e6 + 5;
const int M = N << 1;
const ll inf = 1e18 + 5;
int n, q, flag;
ll x, y, z, l, r, ans;
ll head[N], cnt, ver[M], nxt[M];
ll edge[M], d[N], st[N][22], ok[N];
void add(ll x, ll y, ll z)
{
	ver[++ cnt] = y, edge[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;
}
void dfs1(int x, int fa)
{
	for(int i = head[x]; i; i = nxt[i])
	    if(ver[i] != fa)
	        d[ver[i]] = d[x] + edge[i], dfs1(ver[i], x);
}
void solve_1()// 链 
{
	dfs1(1, 0);
	while(q --)
	{
		l = read(), r = read(), x = read();
		if(x < l) printf("%lld\n", d[l] - d[x]);
		else if(x > r) printf("%lld\n", d[x] - d[r]);
		else puts("0");
	}
	exit(0);
}
void solve_2()// 菊花图 
{
	F(i, 2, n) st[i][0] = edge[head[i]];
	F(j, 1, 20)
	    for(int i = 1; i + (1 << j) - 1 <= n; ++ i)
   		    st[i][j] = min(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
    while(q --)
    {
    	l = read(), r = read(), x = read();
    	int k = log2(r - l + 1); 
    	if(x <= r && x >= l) puts("0");// 如果 x 安全,则输出 0 
    	else if(l == 1) printf("%lld\n", edge[head[x]]);// 如果 1 安全,直接输出到 1 的距离 
		else if(x == 1) printf("%lld\n", min(st[l][k], st[r - (1 << k) + 1][k]));// 如果要求的是 1,则直接找一个最小值 
    	else printf("%lld\n", edge[head[x]] + min(st[l][k], st[r - (1 << k) + 1][k]));// 剩余情况 
	}
	exit(0);
}
void dfs3(ll x, ll fa, ll dis)
{
	if(dis >= ans) return;
	if(x >= l && x <= r)
	{
		ans = min(ans, dis); 
		return;
	}
	for(int i = head[x]; i; i = nxt[i])
	    if(ver[i] != fa)
	    	dfs3(ver[i], x, dis + edge[i]);
}
void solve_3()// 其他
{
	while(q --)
	{
		l = read(), r = read(), x = read();
		ans = inf, dfs3(x, 0, 0);
		printf("%lld\n", ans);
	}
} 
int main()
{
    n = read();
    F(i, 1, n - 1)
	{
		x = read(), y = read(), z = read(), add(x, y, z), add(y, x, z);
		if(abs(x - y) > 1) flag = 1;
	}
	q = read();
	if(! flag) solve_1();
	F(i, 2, n) if(ver[head[i]] != 1) flag = 0;
	if(flag) solve_2();
	solve_3(); 
	return 0;
}
ll read()
{
	ll x = 0, f = 1;
	char c = getchar();
	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;
}

【考试总结】小奇模拟赛

标签:直接   维护   push   当当   类型   双向   getch   之间   ret   

原文地址:https://www.cnblogs.com/Bn_ff/p/12740444.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!