qbxt国庆水题记day2

#qbxt国庆水题记#
#day2#


//100 + 0 + 0 = 100
//除了超水题以外其他的都不会


##Problem 1. video##
Input file: video.in
Output file: video.out
Time limit: 1s
Memory limit: 256M
pluto 喜欢看片,现在他的硬盘里有 n 部片,但是由于他还要把妹,所以看片时间有限,他只能挑出
其中的 k 部片来看,他想知道有多少种不同的选片方案。方案数可能很大,答案 mod 1000000007 输出。
Input
输入文件第一行一个整数 N, 表示这个星球上的总人口。
接下来 N 行,每行一个正整数,表示每个居民的姓名。
Output
输出文件一行一个整数,表示这个星球的价值。
Example
video.in
6 2
video.out
15
Scoring
• 对于 30% 的数据,n ≤10
• 对于 60% 的数据,n ≤3000
• 对于 100% 的数据,n ≤2 ×105

排列组合
根据公式求阶乘,在求下逆元即可

代码

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long

const int inf = 1000000007;
int n,m;
ll p,q;

void exgcd(ll a,ll b,ll &d,ll &x,ll &y) {
	if(!b) {d = a, x = 1, y = 0;return;}
	exgcd(b,a%b,d,y,x);
	y -= x * (a / b);
}

ll inv(ll a, ll b) {
	ll d,x,y;
	exgcd(a,b,d,x,y);
	return (x % b + b) % b;
}

int main() {
	freopen("video.in","r",stdin);
	freopen("video.out","w",stdout);
	cin>>n>>m;
	m = min(m, n-m);
	p = 1, q = 1;
	for(int i = 1; i <= m; i++) p = (p * i) % inf;
	q = inv(p,inf);
	for(int i = n; i >= n - m + 1; i--) q = (q * i) % inf;
	cout<<q<<endl;
	return 0;
}


##Problem 2. chance##
Input file: chance.in
Output file: chance.out
Time limit: 1s
Memory limit: 256M
pluto 去找妹子 ×××约会,然后×××要求和 pluto 玩一个游戏,pluto 赢了才能获得和 ×××
约会的机会。游戏内容为:现在有 N 个袋子 (你可以认为它是哆啦 A 梦的口袋,每个袋里面放着一些
,所以容量十分大,第 i 个袋里面放着编号为 Li 到 Ri 的球 (除编号外完全相同),pluto 需要从每个
袋里面摸出一个球,第 i 个袋子任何一个球被摸到的概率是 1/(Ri −Li + 1),如果 pluto 摸出的球中
有 K% 或以上的球的编号的第一位是 1(比如 11,121,199 的第一位是 1, 而 21,233 第一位就不是 1),那
么 pluto 就将赢得与 ×××约会的机会。现在 pluto 想知道他能人生中第一次与妹子约会的概率有多大。
Input
第一行两个整数 N, K
接下来 N 行,每个两个整数,Li 和 Ri
Output
一行一个实数(保留 7 位⼩数)表示答案
绝对误差不超过 10−6 即视为正确
Example
chance.in
2 50
1 2
9 11
chance.out
0.833333333333333

Scoring
• 对于 100% 的数据,0 ≤k ≤100,0 < Li ≤Ri
• 对于 30% 的数据,n ≤10,Li ≤Ri ≤100
• 对于 60% 的数据,n ≤500,Li ≤Ri ≤2000
• 对于 100% 的数据,n ≤2000,Li ≤Ri ≤1018

首先判断出每个袋子里拿到第一个数字为1的球的概率为pi(很巧妙的判断见"pd()")
dp[i][j] 表示前i个袋子里取到了j个第一个数字为1的球的概率
dp[i][j] = dp[i - 1][j - 1] * pi + dp[i - 1][j] * (1 - pi)
然后根据K%求出至少要多少人,相加即可

//为什么这么多dp
//啊啊啊啊啊

代码

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

const int maxn = 2000 + 100;
int n;
long long l,r;
double k,pi[maxn],f[maxn][maxn];

long long pd(long long x) {
	long long num = 1,sum = 1;
	if(!x) return 0;
	for(int i = 2; i <= 20; i++) {
		num *= 10;
		if(num > x) return sum;
		if(num * 2 > x) return sum + x - num + 1;
		sum += num;
	}
}

int pp(double p) {
	if(p - (int)p == 0) return (int)p;
	else{
		 return (int)(p + 1);
	}
}

int main() {
	freopen("chance.in","r",stdin);
	freopen("chance.out","w",stdout);
	cin>>n>>k;
	for(int i = 1; i <= n; i++)	{
		scanf("%lld%lld",&l,&r);
		pi[i] = (double)(pd(r) - pd(l-1)) / (double)(r - l + 1);
	}
	f[0][0] = 1;
	for(int i = 1; i <= n; i++) {
		for(int j = 0; j <= i; j++) {
			f[i][j] += f[i-1][j] * (1 - pi[i]);
			if(j) f[i][j] += f[i-1][j-1] * pi[i];
		}
	}
	k /= 100;
	int t = pp(n*k);
	double ans = 0;
	for(int i = t; i <= n; i++) {
		ans += f[n][i];
	} 
	printf("%.7f\n",ans);
	return 0;
}


##Problem 3. plutotree##
Input file: plutotree.in
Output file: plutotree.out
Time limit: 2s
Memory limit: 256M
有一棵 n 个节点的树,节点编号为 1 到 n,i 号节点的权值为 Wi。这棵树有些奇怪,它的每一个
叶子节点都是根节点的夫亲 (表示每个叶子节点与根节点之间有一条边权为 0 的边)。我们称这样的树为
pluto 树,根节点编号为 1。我们需要最小化从 u 到 v 的路径 (每条边只能经过一次) 上的节点权值之和,
并且在最小化节点权值之和的同时求这个路径上可能的最大权值。
Input
第一行两个整数 n 和 q,n 表示节点个数,q 表示询问个数。
第二行 n −1 个整数 Ai,表示 i + 1 号节点的夫亲为 Ai
第三行 n 个整数 Wi 表示 i 号节点的权值为 Wi
接下来 q 行,每行两个整数 u,v,表示一组询问
Output
对于每组询问输出两个整数 x, y
x 表⽰ u 到 v 的权值和最小的路径的权值和,y 表⽰这条路径上点权最大值。如果有多个相同权值
和的路径,输出那个点权最大值最大的。
Example
plutotree.in
5 1
1 2 3 4
413 127 263 869 960
1 5
plutotree.out
1373 960

Scoring
• 对于 30% 的数据,n ≤300,q ≤1000
• 对于 50% 的数据,n ≤2000,q ≤10000
• 对于 100% 的数据,n ≤100000,q ≤100000

//树上dp
//不会,暴力也不会打

来自题解
30% 把图建建出来 floyd
60% 把floyd换成spfa或者dijkstra
100% 树形dp
首先答案可能有三种情况:
1.不走叶子到根的边, 那就是一个简单的树上的问题, 两点之间有且仅有一条路径,树上倍增一下就可以把两个问题都解决, 或者闲着蛋疼的可以用树剖

2.从u走到叶子,从叶子到根,从根到v (这种情况要把u、v调换再做一次)
3.从u走到叶子,从叶子到根,从根到另一个叶子节点,再从叶子节点到v

对于第二种、三种情况, 其实关键在于求出每个点到离他最近的叶子的路径,这样的路径有两种可能,一种是向下下走到叶子, 一种是先向上(有可能经过根也可能不经过)走,最终到达叶子
首先树形dp出每个节点向下到离它最近的叶子节点的距离,并同时记录这个路径上的最大值,设为down[i], (down[i]为二元组,<路径权值和, 路径上最大权值>) down[i] 可以从 i号节点的所有儿子转移过来, 转移方程很显然
有了down[i], 就可以dp出我们需要的每个点到最近的叶子节点的路径了,将这个记为dp[i], dp[i]同样是一个二元组,和down[i]一样。 首先dp[1]我们是知道的,就是从根直接走到某一个叶子节点, 其余的dp[i]初值都是down[i], dp[i]可以从i号节点的父亲转移过来,所以这是一个自上而下的dp

考虑到可能爆栈的情况, 建议使用bfs处理树形dp

具体状态如何转移可以参考std

代码std

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;
const int N = 111111;
const int inf = 1 << 20;
int fa[N][20], n, m, maximum[N][20], leaf[N], weight[N];
int dist[N],dep[N];
pair<int, int> dp[N], down[N];
vector<int> adj[N];

void update(pair<int, int> &x, pair<int, int> y) {
	if (x.first != y.first) {
		x = min(x, y);
	} else {
		x = max(x, y);
	}
}

void build() {
	vector<int> queue;
	queue.push_back(1);
	fa[1][0] = 0;
	dep[1] = 1;
	dist[1] = weight[1];
	for (int head = 0; head < (int)queue.size(); head++) {
		int now = queue[head];
		for (int i = 0; i < (int)adj[now].size(); i++) {
			queue.push_back(adj[now][i]);
			dep[adj[now][i]] = dep[now] + 1;
			dist[adj[now][i]] = dist[now] + weight[adj[now][i]];
		}
	}

	for (int j = 1; j <= 18; j++) {
		for (int i = 1; i <= n; i++) {
			fa[i][j] = fa[fa[i][j - 1]][j - 1];
			maximum[i][j] = max(maximum[i][j - 1], maximum[fa[i][j - 1]][j - 1]);
		}
	}

	for (int i = (int)queue.size() - 1; i >= 0; i--) {
		int x = queue[i];
		update(down[fa[x][0]], make_pair(down[x].first + weight[fa[x][0]], max(down[x].second, weight[fa[x][0]])));
	}
}

void DP() {
	vector<int> queue;
	queue.push_back(1);	
	update(dp[1], down[1]);
	for (int head = 0; head < (int)queue.size(); head++) {
		int now = queue[head];
		for (int i = 0; i < (int)adj[now].size(); i++) {
			int to = adj[now][i];
			dp[to] = down[to];
			update(dp[to], make_pair(dp[now].first + weight[to], max(weight[to], dp[now].second)));
			queue.push_back(to);
		}
	}
}

pair<int, int> query(int x, int y) {
	if (dep[x] < dep[y]) swap(x, y);
	int fx = x, fy = y;
	int maxd;
	int ret1 = 0, ret2 = 0;
	for (maxd = 0; (1 << maxd) <= dep[x]; maxd++);
	maxd--;
	ret1 = max(maximum[x][0], maximum[y][0]);
	for (int i = maxd; i >= 0; i--) {
		if (dep[fa[x][i]] >= dep[y]) {
			ret1 = max(ret1, maximum[x][i]);
			x = fa[x][i];
		}
 	}
 	ret1 = max(ret1, maximum[x][0]);
 	ret1 = max(ret1, maximum[y][0]);
 	if (x == y) {
 		ret2 = dist[fx] - dist[fy] + weight[fy]; 
 		return make_pair(ret1, ret2);
 	}

 	for (int i = maxd; i >= 0; i--) {
 		if (fa[x][i] != 0 && fa[x][i] != fa[y][i]) {
 			ret1 = max(ret1, maximum[x][i + 1]);
 			ret1 = max(ret1, maximum[y][i + 1]);
 			x = fa[x][i];
 			y = fa[y][i];
 		}
 	}
 	ret1 = max(ret1, maximum[x][0]);
 	ret1 = max(ret1, maximum[y][0]);
 	ret1 = max(ret1, maximum[fa[x][0]][0]);
 	ret2 = dist[fx] + dist[fy] - dist[fa[x][0]] * 2 + weight[fa[x][0]];
 	return make_pair(ret1, ret2);
}
void add(int x, int y) {
	adj[x].push_back(y);
}
void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) adj[i].clear();
	for (int i = 1; i <= n - 1; i++) {
		int x;
		scanf("%d", &x);
		add(x, i + 1);
		fa[i + 1][0] = x;
	}
	for (int i = 1; i <= n; i++) {
		if (adj[i].size() == 0) leaf[i] = 1;
		else leaf[i] = 0;
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", &weight[i]);
		maximum[i][0] = weight[i];
	}

	for (int i = 1; i <= n; i++) {
		dp[i] = make_pair(inf, 0);
		down[i] = make_pair(inf, 0);
	}

	for (int i = 1; i <= n; i++) {
		if (leaf[i]) {
			down[i] = make_pair(weight[i], weight[i]);
			update(dp[1], make_pair(weight[1] + weight[i], max(weight[1], weight[i])));
		}
	}
	build();	
	DP();
	for (int i = 1; i <= m; i++) {
		pair<int, int> tmp, ans;
		int u, v;
		scanf("%d%d", &u, &v);
		tmp = query(u, v);
		ans = make_pair(tmp.second, -tmp.first);
		tmp = make_pair(dp[u].first + dist[v], -max(dp[u].second, maximum[v][18]));
		ans = min(ans, tmp);
		tmp = make_pair(dp[v].first + dist[u], -max(dp[v].second, maximum[u][18]));
		ans = min(ans, tmp);
		tmp = make_pair(dp[u].first + dp[v].first + weight[1], -max(max(dp[u].second, dp[v].second), weight[1]));
		ans = min(ans, tmp);
		printf("%d %d\n", ans.first, -ans.second);
	} 

}

int main() {
	freopen("plutotree.in", "r", stdin);
	freopen("plutotree.out", "w", stdout);

	int tests = 1;
	for (int i = 1; i <= tests; i++) {
		solve();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值