6月27训练赛

文章目录

    • A hdu 4586
    • B hdu 4587
    • C hdu 5355
    • D hdu 6191
    • F ural 1651
    • G ural 1658

A hdu 4586

题意:给定一个骰子,有若干个面,当掷到某个面得到一定的钱。某些面可以再掷一次,问掷一次获得钱的期望

投一次色子的期望是: s u m n \frac{sum}{n} nsum
2次的期望: s u m n + m n ∗ s u m n \frac{sum}{n} + \frac{m}{n}*\frac{sum}{n} nsum+nmnsum
n次的概率是: s u m n + m n ∗ s u m n + . . . + ( m n ) n − 1 ∗ s u m n \frac{sum}{n} + \frac{m}{n}*\frac{sum}{n} + ... + (\frac{m}{n})^{n-1}*\frac{sum}{n} nsum+nmnsum+...+(nm)n1nsum
等比数列前n项和,设 s u m n = p , m n = q ( 0 < = q < = 1 ) \frac{sum}{n}=p, \frac{m}{n}=q (0<=q<=1) nsum=p,nm=q(0<=q<=1) ( 1 − q n ) ∗ p 1 − q \frac{(1-q^n)*p}{1-q} 1q(1qn)p
p == 0, ans = 0; q == 1, ans = inf, q < 1, q n q^n qn ->无穷小, ans = p 1 − q \frac{p}{1-q} 1qp

#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define pb push_back
#define mk make_pair
const int N = 200+10;
const int mod = 1e9+7;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}

ll qpow(ll a, int x){
	ll res = 1;
	while(x){
		if(x&1) res = res * a % mod;
		a = a * a % mod;	x >>= 1;
	}
	return res;
}

int n, a[N], m, b[N];


int main(){
	while(~scanf("%d", &n)){
		double sum = 0;
		for(int i = 1; i <= n; i++){
			a[i] = read();
			sum += a[i];
		}
		double p = sum / n;
		m = read();
		int cnt = 0;
		for(int i = 1; i <= m; i++){
			b[i] = read();
		}
		double q = m*1.0 / n;
		if(p < 1e-10){
			printf("0.00\n");
		}
		else if(q < 1 + 1e-10 && q > 1 - 1e-10){
			printf("inf\n");
		}
		else{
			printf("%.2llf\n", p / (1 - q));
		}

	}
	return 0;
}

B hdu 4587

题意:给一个无向图,求删掉两个点之后最多联通块数量。

枚举删除一个点,然后tarjan求割点,答案为联通块数 + cut[j],取max

#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e4+10;
const int mod = 1e9+7;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}

int n, m;
struct Edge{
	int to, next;
}e[N];
int head[N], tot = 0;
void addEdge(int u, int v){
	e[tot] = {v, head[u]};
	head[u] = tot++;
}

int low[N], dfn[N], cut[N], index;

void init(){
	memset(low, 0, sizeof(int)*(n+1));
	memset(dfn, 0, sizeof(int)*(n+1));
	memset(cut, 0, sizeof(int)*(n+1));
	index = 0;
}

void tarjan(int u, int fa, int del){
	low[u] = dfn[u] = ++index;
	int pre_cnt = 0, son = 0;
	for(int i = head[u]; i != -1; i = e[i].next){
		int v = e[i].to;
		if(v == del) continue;
		if(v == fa && pre_cnt == 0) {
			pre_cnt++; continue;
		}
		if(!dfn[v]){
			son++;
			tarjan(v, u, del);
			low[u] = min(low[u], low[v]);
			if(low[v] >= dfn[u]){
				cut[u] ++;
			}
		}
		else if(u != fa && low[u] > dfn[v]){
			low[u] = dfn[v];
		}
	}
	if(u == fa) { //根节点
		cut[u] = son - 1;
	}
}


int main(){
	while(~scanf("%d %d", &n, &m)){
		memset(head, -1, sizeof(int)*(n+1));
		tot = 0;
		int u, v;
		for(int i = 0; i < m; i++){
			scanf("%d %d", &u, &v);
			addEdge(u, v);
			addEdge(v, u);
		}
		int ans = 0;
		for(int i = 0; i < n; i++){
			init();
			int cnt = 0;
			for(int j = 0; j < n; j++){
				if(i == j) continue;
				if(!dfn[j]){
					tarjan(j, j, i);
					cnt++;
				}
			}
			for(int j = 0; j < n; j++){
				if(i == j) continue;
				ans = max(ans, cnt + cut[j]);
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

C hdu 5355

题意:把 1 − n 1-n 1n n n n个数,分成 m m m堆,使得 m m m堆数的和相等。

无解的情况肯定是 s u m m < n ∣ ∣ s u m % m ! = 0 \frac{sum}{m} < n || sum\%m != 0 msum<nsum%m!=0
将较大的数,每 2 × m 2\times m 2×m为一组,放入 m m m堆中,从小到大相应配对,显然和相等
当剩下的数小于4m-1时,暴力匹配,
当n!= m时,要想分成m堆, n最小为2m-1,1 和 n-1一组, 2和n-2一组。。。n单独为一组,所以当每2m为一组时,只有n-2m >= 2m-1,才能分出一组, n < 4m-1时暴力匹配。

#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;

int t, n, m;
vector<int> g[N];

void printYes(){
	printf("YES\n");
	for(int i = 0; i < m; i++){
		printf("%d ", g[i].size());
		for(int j = 0; j < g[i].size(); j++){
			printf("%d%c", g[i][j], " \n"[j == g[i].size()-1]);
		}
	}
}

int vis[100], ave;
int dfs(int cur, int sum, int num){
	if(cur == m){
		return 1;
	}
	for(int i = n; i >= num; i--){
		if(vis[i]) continue;
		if(sum + i == ave){
			vis[i] = 1;
			if(dfs(cur+1, 0, 1)){
				g[cur].push_back(i);
				return 1;
			}
			vis[i] = 0;
			return 0;
		}
		else{
			vis[i] = 1;
			if(dfs(cur, sum+i, i)){
				g[cur].push_back(i);
				return 1;
			}
			vis[i] = 0;
		}
	}
	return 0;
}

int main(){
	scanf("%d", &t);
	while(t--){
		scanf("%d %d", &n, &m);
		ll sum = 1ll * (1 + n) * n / 2;
		if(sum%m || sum / m < n){
			printf("NO\n");
		}
		else{
			for(int i = 0; i <= m; i++){
				g[i].clear();
			}
			while(n -2*m >= 2*m - 1){
				for(int i = 0; i < m; i++){
					g[i].push_back(n-i);
					g[i].push_back(n-2*m+i+1);
				}
				n -= 2*m;
			}
			sum = 1ll * (1 + n) * n / 2;
			ave = sum / m;
			memset(vis, 0, sizeof(vis));
			dfs(0, 0, 1);
			printYes();
		}
	}
	return 0;
}

D hdu 6191

题意:给一棵树,每个节点有个权值。若干次查询,每次查询一棵子树中的数与一个数异或最大为多少。

来自于:xls
把每个点权拆成二进制构造字典树,假设每次查找的是 1,x,那么直接从字典树中找最大值(默认这个大家都会),如果是 u,x,那么我只需要只保存 u 子树的字典树,这个时候我们借助dfs序和可持久化操作建字典树,第 i 颗字典树保存字典序 1 到 i 个点的信息,id [ u ]保存 u 节点的dfs序编号,sz[ u ]保存 u 子树的大小,每次查询 u,x,通过 r t [ i d [ u ] + s z [ u ] − 1 ] − r t [ i d [ u ] − 1 ] rt[ id[u] + sz[u] - 1] - rt[ id[u] - 1] rt[id[u]+sz[u]1]rt[id[u]1]就可以得到我们想要的字典树,难点解决

#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}

int n, q, a[N];
struct Edge{
	int to, next;
}e[N];
int head[N], tot = 0;
void addEdge(int u, int v){
	e[tot] = {v, head[u]};
	head[u] = tot++;
}

int rt[N], ls[N*31], rs[N*31], sum[N*31],cnt = 0;
void up(int &o, int pre, int k, int v){
	o = ++cnt;
	ls[o] = ls[pre];
	rs[o] = rs[pre];
	sum[o] = sum[pre] + 1;
	if(k < 0)
		return ;
	if(v & (1<<k))
		up(rs[o], rs[pre], k-1, v);
	else 
		up(ls[o], ls[pre], k-1, v);
}

int id[N], sz[N], index = 0;
void dfs(int u){
	id[u] = ++index;
	sz[u] = 1;
	up(rt[index], rt[index-1], 29, a[u]);
	for(int i = head[u]; i != -1; i = e[i].next){
		int v = e[i].to;
		dfs(v);
		sz[u] += sz[v];
	}
}

int qu(int o, int pre, int k, int v){
	if(k < 0)
		return 0;
	if(v & (1<<k)){
		if(sum[ls[o]] > sum[ls[pre]])
			return (1<<k) + qu(ls[o], ls[pre], k-1, v);
		else
			return qu(rs[o], rs[pre], k-1, v);
	}
	else{
		if(sum[rs[o]] > sum[rs[pre]])
			return (1<<k) + qu(rs[o], rs[pre], k-1, v);
		else 
			return qu(ls[o], ls[pre], k-1, v);
	}
}


int main(){
	while(~scanf("%d %d", &n, &q)){
		memset(head, -1, sizeof(int)*(n+1));
		tot = 0; index = 0; cnt = 0;
		for(int i = 1; i <= n; i++){
			a[i] = read();
		}
		int fa;
		for(int i = 2; i <= n; i++){
			fa = read();
			addEdge(fa, i);
		}
		dfs(1);
		int u, x;
		while(q--){
			u = read(); x = read();
			printf("%d\n", qu(rt[id[u]+sz[u]-1], rt[id[u]-1], 29, x));
		}
		for(int i = 0; i <= cnt; i++){
			sum[i] = ls[i] = rs[i] = 0;
		}
	}
	return 0;
}

F ural 1651

题意:给出一条链,求链上边的子集,使得起点到终点了联通。子链中边出现的顺序必须和原链一样,子链最短。

建图,跑最短路,原链中给每个点编号 1-n, 并且按照新编号连边,然后如果前面出现了这个点,那么就从上一个点连一条边权为0的边到现在这个点,这样建图跑最短路,边出现的顺序就会和原链一样。

#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}

ll qpow(ll a, int x){
	ll res = 1;
	while(x){
		if(x&1) res = res * a % mod;
		a = a * a % mod;	x >>= 1;
	}
	return res;
}

int n;
int a[N];
struct Edge{
	int to, next, w;
}e[2*N];
int head[N], tot = 0;
void addEdge(int u, int v, int w){
	e[tot] = {v, head[u], w};
	head[u] = tot++;
}

int d[N], vis[N], pre[N];
priority_queue< pair<int, int> > q;
void Dij(){
	for(int i = 1; i <= n; i++){
		d[i] = 1e9;
		vis[i] = pre[i] = 0;
	}
	d[1] = 0;
	q.push(mk(0, 1));
	while(!q.empty()){
		int u = q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i = head[u]; i != -1; i = e[i].next){
			int v = e[i].to;
			if(d[v] > d[u] + e[i].w){
				d[v] = d[u] + e[i].w;
				pre[v] = u;
				q.push(mk(-d[v], v));
			}
		}
	}
}

int main(){
	n = read();
	memset(head, -1, sizeof(int)*(n+1));
	tot = 0;
	int u, v;
	for(int i = 1; i <= n; i++){
		a[i] = read();
	}
	vis[a[1]] = 1;
	for(int i = 1; i < n; i++){
		addEdge(i, i+1, 1);
		if(vis[a[i+1]]) {
			addEdge(vis[a[i+1]], i+1, 0);
			//cout << vis[a[i+1]] << " " << i+1 << "..\n";
		}
		vis[a[i+1]] = i+1;
	}
	Dij();
	vector<int> ans;
	int t = n;
	while(t){
		if(!ans.size() || ans[ans.size()-1] != a[t])
			ans.push_back(a[t]);
		t = pre[t];
	}
	for(int i = ans.size()-1; i >= 0; i--){
		printf("%d%c", ans[i], " \n"[i==0]);
	}
	//cout << d[n] << "\n";
	return 0;
}

G ural 1658

题意:给出一个数的数字和以及数字的平方和,求最小满足题意的数。

因为最多只有100位,所以s1 <= 900, s2 <= 8100,
dp[i][j][0] 表示 和为i 平方和为j 的数最小长度,dp[i][j][1] 存的是当前选的数
从小往大选,严格小于才更新,这样可以保证长度最小的同时,值最小。

#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;

int t, s1, s2;
int dp[901][8101][2];

void init(){
	for(int i = 0; i <= 900; i++){
		for(int j = 0; j <= 8100; j++){
			dp[i][j][0] = 200;
		}
	}
	dp[0][0][0] = 0;
	for(int i = 1; i <= 900; i++){
		for(int j = 1; j <= 8100; j++){
			for(int k = 1; k <= 9; k++){
				if(i - k >= 0 && j - k*k >= 0){
					if(dp[i][j][0] > dp[i-k][j-k*k][0] + 1){
						dp[i][j][0] = dp[i-k][j-k*k][0] + 1;
						dp[i][j][1] = k;
					}
				}
			}
			
		}
	}
}

int main(){
	init();
	scanf("%d", &t);
	while(t--){
		scanf("%d %d", &s1, &s2);
		if(s1 > 900 || s2 > 8100 || dp[s1][s2][0] > 100){
			printf("No solution\n");
		}
		else {
			vector<int> v;
			while(dp[s1][s2][0]){
				int p = dp[s1][s2][1];
				v.push_back(dp[s1][s2][1]);
				s1 -= p; s2 -= p*p;
			}
			sort(v.begin(), v.end());
			for(int i = 0; i < v.size(); i++){
				printf("%d", v[i]);
			}
			printf("\n");
		}
	}
	return 0;
}

你可能感兴趣的:(训练赛)