2021.09.13膜你赛

2021.09.13膜你赛

T1

Description

小迟生活的城市是一棵树(树指的是一个含有 \(n\) 个节点以及 \(n-1\) 条边的无向连通图),节点编号从 \(1\)\(n\),每条边拥有一个权值 \(value\),表示通过这条边的时候你需要交纳的金钱(注意,有可能这个值为负数,也就是说你经过这条边的时候你可以赚钱)

小迟是一个杰出的马路工,他居住在节点 \(s\),他能够选择任意一个节点 \(m\),并将从节点 \(s\) 到节点 \(m\) 的简单路径(简单路径指的是不经过同一个节点两次)上的所有边的权值都修改为 \(0\).

现在小迟获得 \(q\) 个请求,每个请求都是以 \(a \ b\) 的形式给出,表示小迟的好朋友小早希望从节点 \(a\) 走简单路径到节点 \(b\),小迟希望能最小化小早需要缴纳的钱。

需要注意的是,小迟获得的这 \(q\) 个请求是相互独立的,也就是说您只需要对于每一个请求,决定小迟的一个修路放案,使得小早需要缴纳的钱尽可能的少。

Solution

树剖找最大值。

考试的时候写的找两个点到他们 Lca 的和,让大的那个为 0,也就是输出较小边。

考完试发现不对劲,因为可能出现这样的情况

按照错误的写法,会把 \(lca\to v\) 的路径都变成 0,但是下图的路径变成 0 会使得花费最小。

所以在两个路径上找 \(lca \to point\) 的最大值,取较大值,用总路径的长度减去这个大的就是最下的花费。

Code

由于我的怎么调都不对,所以放的 BS 的码。

/*
  Source: road
  有负边
  a b 在 s 子树中: a b 两点到 lca 的路径中较大值 均为负值则不操作
  a b 与 s 没关系: 同上一种
  s 在 a 或 b 的子树中: 路径与 0 取 min
  s 在 a 到 b 的路径上: a 到 s 与 b 到 s 的路径中较大的与 0 取 min 
  
  可以直接把 s 当根就没这些事了
  直接考虑 a b 的 lca 即可 
  
  有点问题... 有负边 
  考虑开始消去的位置 
  首先有个暴跳的暴力
  
  把每个点到根的距离看做这个点的点权 树剖维护路径点权最大值以及最大值的点的编号 
  
  一个半小时 拍上了 
*/
#include<cstdio>
#include<cstring>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 2e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen("road.in", "r", stdin);
	freopen("road.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, q, s, fa[B], dep[B], dis[B], pos[B];
struct edge {int v, w, nxt;} e[B << 1];
int head[B], ecnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {
		int l, r, max, id;
		node() {l = r = id = 0; max = -INF;}
	} t[B << 2];
	node operator + (node x, node y) {
		node z; z.l = x.l; z.r = y.r;
		if(x.max > y.max) z.max = x.max, z.id = x.id;
		else z.max = y.max, z.id = y.id;
		return z;
	}
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; if(l == r) {t[p].max = dis[pos[l]]; t[p].id = pos[l]; return ;}
		build(ls(p), l, mid); build(rs(p), mid + 1, r); t[p] = t[ls(p)] + t[rs(p)];
	}
	node query(int p, int l, int r) {
		if(l <= t[p].l && t[p].r <= r) return t[p];
		if(l <= mid) if(r > mid) return query(ls(p), l, r) + query(rs(p), l, r);
		else return query(ls(p), l, r); else return query(rs(p), l, r);
	}
}
namespace Cut {
	int siz[B], son[B], top[B], dfn[B], kcnt;
	void dfs1(int u, int pre) {
		siz[u] = 1; fa[u] = pre; dep[u] = dep[pre] + 1;
		for(int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].v, w = e[i].w;
			if(v == pre) continue;
			dis[v] = dis[u] + w; dfs1(v, u); siz[u] += siz[v];
			if(!son[u] || siz[son[u]] < siz[v]) son[u] = v;
		}
	}
	void dfs2(int u, int tp) {
		top[u] = tp; dfn[u] = ++kcnt; pos[kcnt] = u;
		if(!son[u]) return ; dfs2(son[u], tp);
		for(int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].v; if(v == fa[u] || v == son[u]) continue;
			dfs2(v, v);
		}
	}
	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]];
		}
		return dep[x] < dep[y] ? x : y;
	}
	int query(int x, int y) {
		Seg::node res;
		while(top[x] != top[y])
		{
			if(dep[top[x]] < dep[top[y]]) Swap(x, y);
			res = res + Seg::query(1, dfn[top[x]], dfn[x]);
			x = fa[top[x]];
		}
		if(dfn[x] > dfn[y]) Swap(x, y);
		res = res + Seg::query(1, dfn[x], dfn[y]);
		return res.id;
	}
}
void work1() {
	for(int i = 1; i ^ q + 1; ++i)
	{
		int x = read(), y = read(), z = Cut::LCA(x, y);
		if(z == x || z == y)
		{
			if(dep[x] < dep[y]) Swap(x, y); int min = 0, tmp = x;
			for(int j = x; j != fa[y]; j = fa[j])
				if(dis[x] - dis[j] < min) min = dis[x] - dis[j], tmp = j;
			Print(dis[x] - dis[tmp]); pn;
		}
		else
		{
			int min1 = 0, tmp1 = x, min2 = 0, tmp2 = y;
			for(int j = x; j != fa[z]; j = fa[j])
				if(dis[x] - dis[j] < min1) min1 = dis[x] - dis[j], tmp1 = j;
			for(int j = y; j != fa[y]; j = fa[j])
				if(dis[y] - dis[j] < min2) min2 = dis[y] - dis[j], tmp2 = j;
			int res1 = dis[x] - dis[tmp1] + dis[y] - dis[z], res2 = dis[y] - dis[tmp2] + dis[x] - dis[z];
			Print(Min(res1, res2)); pn;
		}
	}
}
void work2() {
	for(int i = 1; i ^ q + 1; ++i)
	{
		int x = read(), y = read(), z = Cut::LCA(x, y);
		if(x == y) Print(0), pn;
		else if(z == x || z == y)
		{
			if(dep[x] < dep[y]) Swap(x, y);
			int tmp = Cut::query(x, y);
			Print(dis[x] - dis[tmp]); pn;
		}
		else
		{
			int tmp1 = Cut::query(x, z), tmp2 = Cut::query(y, z);
			int res1 = dis[x] - dis[tmp1] + dis[y] - dis[z], res2 = dis[y] - dis[tmp2] + dis[x] - dis[z];
			Print(Min(res1, res2)); pn;
		}
	}
}
void Main() {
	File();
	n = read(); q = read(); s = read();
	for(int i = 1, x, y, z; i ^ n; ++i) x = read(), y = read(), z = read(), add_edge(x, y, z), add_edge(y, x, z);
	Cut::dfs1(s, 0); Cut::dfs2(s, s); Seg::build(1, 1, n);
//	if(n * q < D) work1();
//	else work2();
	work2();
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}

T2

Description

随着川普的支持率逐渐下降,让川普下台的呼声越来越高,国会被迫进行了一次表决,每一位议员都有权利选择支持持川普还是反对川普,若反对的议员严格超过一半,则川普只能卸任。议员共 \(N\) 个,每个议员有两个属性:
威望度 \(W_i\) 和忠诚度 \(H_i\)。在表决的时候,每个议员⽀持川普的概率为 \(\frac{H_i}{100}\)

Kano 是一位腰缠万贯的企业家,出于个人原因,他想让川普下台。他拿出了 \(K\) 百万元,想要通过贿赂议员的形式来增加川普下台的可能性。每个议员每收下 1 百万元,那么他的忠诚度便会减少 \(10\),减少到 \(0\) 的时候就
不可再减少了。

即便这样,表决还是可能以支持的结果收场,那么 Kano 将派出刺客,暗杀所有投了支持票的议员。设所有投了支持票的议员的威望度总和为 \(S\),则成功暗杀的概率为 \(\frac{A}{A+S}\)\(A\) 为常数)。若暗杀失败,则 Kano 也将会被捕。
现在 Kano 想知道,在最优策略下,被捕的可能性是多少?

Solution

考试的时候因为这 k 给谁是有方法的,然后就自闭了。

先搜这 k 百万的分配方案,然后再搜怎么投票。

Code

/*
* @Author: smyslenny
* @Date:    2021.09.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=998244353;
const int M=1e3+5;
int n,k;
double a,cnt,Ans;
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
struct ed{
    int w,h;
}pep[M];

void dfs_2(int js,double p,int num,int sum)
{
	if(js>n)
	{
		if(num>0) cnt+=p*a/(a+sum);
		else cnt+=p;
	}
	else
	{
		dfs_2(js+1,p*max(0,pep[js].h)/100,num+1,sum+pep[js].w);
		dfs_2(js+1,p*(100-max(0,pep[js].h))/100,num-1,sum);
	}
}
void check()
{
	cnt=0;
	dfs_2(1,1.0,0,0);
	Ans=max(Ans,cnt);
}

void dfs(int l,int r,int K)
{
	check();
	if(K==0) return;
	for(int i=l;i<=r;i++)
	{
		pep[i].h-=10;
		dfs(i,r,K-1);
		pep[i].h+=10;
	}
}
int main()
{
//	freopen("bride.in","r",stdin);
//	freopen("bride.out","w",stdout);
    n=read(),k=read();scanf("%lf",&a);
    for(int i=1;i<=n;i++)
        pep[i].w=read(),pep[i].h=read();
    dfs(1,n,k);
    printf("%.6lf\n",1.0-Ans);
}
/*
5 6 80
12 90
15 80
22 70
45 60
147 50
*/

T3

Description

其实 Kano 曾经到过由乃山,当然这名字一看山主就是 Yuno 嘛。

当年 Kano 看见了由乃山,内心突然涌出了一股杜甫会当凌绝顶,一览众山小的豪气,于是毅然决定登山。但是 Kano 总是习惯性乱丢垃圾,增重环卫工人的负担,Yuno 并不想让 Kano 登山,于是她果断在山上设置了结界……

Solution

题目就是找一个被多少个不同的数经过,一个数会对当前点到根节点的路径做贡献。

对于每一种标记 将需要打标记的点按照 \(dfs\) 序排序 依次打上加一的标记 除第一个点以外 每个点在打完加一标记之后再在自身与上一个打标记的点的 \(LCA\) 的位置打上减一标记 这样就保证了贡献不会算重

处理完所有标记后 遍历一遍树 统计节点的权值即为该点答案

Code

#include<bits/stdc++.h>
using namespace std;
const int M=200010;
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
struct node{
	int g;//地点
	int d;//编号
}b[M];
int f[M][20],d[M],book[M],st[M],a[M],times,v[M];
struct ed{
	int u,v,w,net;
}edge[M<<1];
int m,n,tot,t,ans;
int x,y,z;
queue<int> q;
int head[M],num;
void addedge(int u,int v)
{
	edge[++num].u=u;edge[num].v=v;edge[num].net=head[u];head[u]=num;
}
void dfs(int x)
{//确定dfs序 
	v[x]=1;
	st[x]=++times;
	for(int i=head[x];i;i=edge[i].net)
	{
		int y=edge[i].v;
		if(v[y]) continue;
		dfs(y);
	}
}
void bfs()
{//lca的预处理 
	q.push(0);d[0]=1;
	while(q.size()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=edge[i].net)
		{
			int y=edge[i].v;
			if(d[y]) continue;
			d[y]=d[x]+1;
			f[y][0]=x;
			for(int j=1;j<=t;++j){
				f[y][j]=f[f[y][j-1]][j-1];
			}
			q.push(y);
		}
	}
}
bool cmp(node a,node b)
{//排序 
	if(a.d==b.d) return st[a.g]<st[b.g];
	else return a.d<b.d;
}
int lca(int x,int y)
{//lca 
	if(d[x]>d[y]) swap(x,y);
	for(int i=t;i>=0;--i)
		if(d[f[y][i]]>=d[x])
			y=f[y][i];
	if(x==y) return x;
	for(int i=t;i>=0;--i)
	{
		if(f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0];
}
void find(int x)
{
	v[x]=1;
	for(int i=head[x];i;i=edge[i].net){
		int y=edge[i].v;
		if(v[y]) continue;
		find(y);
		book[x]+=book[y];
	}
}
int main()
{
	n=read(),m=read();
	t=((int)(log(n)/log(2)))+1;
	for(int i=1;i<n;++i)//建边 
		a[i]=read(),addedge(a[i],i),addedge(i,a[i]);
	for(int i=1;i<=m;++i)
		scanf("%d %d",&b[i].g,&b[i].d);
	dfs(0);
	bfs();
	sort(b+1,b+1+m,cmp);
	int pre=-1;
	for(int i=1;i<=m;++i)
	{
		int x=b[i].g;
        book[x]++;
        if(pre!=-1) book[lca(pre,x)]--;
        pre=x;
        if(i<m&&b[i].d!=b[i+1].d)pre=-1;
	}
	memset(v,0,sizeof(v));
	find(0);
	for(int i=0;i<n;++i){
		printf("%d\n",book[i]);
	}
	return 0;
}


本欲起身离红尘,奈何影子落人间。
原文地址:https://www.cnblogs.com/jcgf/p/15319932.html