恩偶爱皮模拟尸体-33

水博客太快乐了。。。。

RT

烤场

先看了三题,发现 \(T2\) 是个裸的线段树合并, \(T3\) 显然应该是一个状压,然而并不像是本蒟蒻会做的。。。

于是先水了 \(T2\) 的线段树合并,然后就不知道该去干啥。。。
水了 \(T1\)\(xin\_\ team\) 。。。
去干 \(T3\) 然后不知道怎么设计状态(还是太菜了,题做的少。。。
一直到考试结束也没想出来什么有用的东西。。。

分数

预估 : \(t1\ 20pts\ +\ t2\ 100pts\ +\ t3\ 0pts\ =\ 120pts\)
实际 : \(t1\ 20pts\ +\ t2\ 100pts\ +\ t3\ 0pts\ =\ 120pts\)

题解

A. Hunter

概率与期望着实一直是我的弱点。。。
实际上这题就是运用了一个简单的转化,不去求 \(1\) 号到底是第几轮死的,而是求其余每个猎人在他之前死的概率,则他死的轮数就是在他死之前死的猎人的数量加一。。
而每个猎人在他之前死的概率很好求,显然是 \(\frac{w_{i}}{w_{i}+w_{1}}\)。。。。
根据:
\(E(x+y)\ =\ E(x)\ +\ E(y)\)
\(\sum_{i=1}^{n} \frac{w_{i}}{w_{i}+w_{1}}\) 即可。。。

code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10, mod=998244353;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
ll qp(ll n, ll m){
	ll ans=1;
	while(m){
		if(m&1) ans*=n, ans%=mod;
		m>>=1; n*=n; n%=mod;
	}
	return ans;
}
int n;
int w[N];
ll ans;
int main(void){
	n=read();
	for(int i=1; i<=n; ++i) w[i]=read();
	for(int i=2; i<=n; ++i){
		ans+=(w[i]*qp(w[i]+w[1], mod-2))%mod;
		ans%=mod;
	}
	printf("%lld\n", ans+1);
	return 0;
}

B. Defence

线段树合并的裸题,不熟悉线段树合并可以拿来练练手。。。

code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10, INF=0x3f3f3f3f;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, m, q, rt[N], tot, ans[N];
vector<int> l[N];
struct TRE{
	int l, r, l1, r1, lm, rm;
	int ls, rs;
}t[N<<5];
inline void upd(int p){
	int ls=t[p].ls, rs=t[p].rs;
	t[p].l1=ls ? t[ls].l1 : t[rs].l1;
	t[p].r1=rs ? t[rs].r1 : t[ls].r1;
	t[p].lm=INF;
	if(ls&&rs) t[p].lm=t[ls].r1, t[p].rm=t[rs].l1;
	if(ls&&t[ls].rm-t[ls].lm>t[p].rm-t[p].lm) t[p].rm=t[ls].rm, t[p].lm=t[ls].lm;
	if(rs&&t[rs].rm-t[rs].lm>t[p].rm-t[p].lm) t[p].rm=t[rs].rm, t[p].lm=t[rs].lm;
}
void add(int &p, int l, int r, int x){
	if(!p) p=++tot; t[p].l=l, t[p].r=r;
	if(l==r) { t[p].l1=t[p].r1=t[p].lm=t[p].rm=l; return; }
	int mid=(l+r)>>1;
	if(x<=mid) add(t[p].ls, l, mid, x);
	else add(t[p].rs, mid+1, r, x); upd(p);
}
void merge(int &l, int r){
	if(!l) { l=r; return; }
	if(t[l].l==t[l].r) return;
	if(t[r].ls) merge(t[l].ls, t[r].ls);
	if(t[r].rs) merge(t[l].rs, t[r].rs);
	if(t[l].l!=t[l].r) upd(l);
}
void dfs(int u, int fa){
	for(int v : l[u]){
		if(v==fa) continue;
		dfs(v, u);
		if(rt[v]) merge(rt[u], rt[v]);
	}
	if(!rt[u]) { ans[u]=-1; return; }
	ans[u]=(t[rt[u]].l1-1)+(m-t[rt[u]].r1);
	ans[u]+=max(t[rt[u]].rm-t[rt[u]].lm-1-ans[u], 0);
}
int main(void){
	n=read(), m=read(), q=read();
	int x, y;
	for(int i=1; i<n; ++i){
		x=read(), y=read();
		l[x].push_back(y);
		l[y].push_back(x);
	}
	for(int i=1; i<=q; ++i){
		x=read(), y=read();
		add(rt[x], 1, m, y);
	}
	dfs(1, 0);
	for(int i=1; i<=n; ++i) printf("%d\n", ans[i]);
	return 0;
}

C. Connect

看这数据范围就是状压。。。

然而这状态着实难以设计。。。。
考虑题目的含义,要求最终只有 \(1\)\(n\) 只有一条链,则显然,若链外的点要与链连接的话,显然只能与链上的一个点连边,所以不妨设 \(f_{s,u}\) ,表示当前选过的点集为 \(S\) ,目前链的结尾是 \(u\) 时的最大权值和。。。
考虑更新,显然每次可以枚举一个点,将其接在\(u\) 后,链长加一,也可以枚举一个点集,让这个点集只与点 \(u\) 相连。。。
预处理出每个点与一个点集之间所有点连边,以及任意一个点集间两两连边的权值。。。

code
#include<bits/stdc++.h>
using namespace std;
const int N=16, INF=0x3f3f3f3f;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, m;
int g[N][N];
int sum[1<<N], to[N][1<<N], f[1<<N][N];
int main(void){
	n=read(), m=read();
	int x, y, z;
	for(int i=1; i<=m; ++i){
		x=read(), y=read(), z=read();
		g[x][y]=g[y][x]=z;
	}
	for(int i=1; i<1<<n; ++i) for(int j=1; j<=n; ++j){
		if(!(i&(1<<(j-1)))) continue;
		for(int k=j+1; k<=n; ++k) if(i&(1<<(k-1))) sum[i]+=g[j][k];
	}
	for(int i=1; i<=n; ++i)	for(int j=1; j<1<<n; ++j){
		if(j&(1<<(i-1))) continue;
		for(int k=1; k<=n; ++k)	if(j&(1<<(k-1))) to[i][j]+=g[i][k];
	}
	memset(f, -0x3f, sizeof f); f[1][1]=0;
	for(int i=1; i<1<<n; ++i){
		for(int j=1; j<=n; ++j){
			if(f[i][j]==-INF) continue; int s=i^((1<<n)-1);
			for(int k=1; k<=n; ++k) if(!(i&(1<<(k-1)))&&g[j][k]) f[i|(1<<(k-1))][k]=max(f[i|(1<<(k-1))][k], f[i][j]+g[j][k]);
			for(int k=s; k; k=(k-1)&s) if(to[j][k]) f[i|k][j]=max(f[i|k][j], f[i][j]+to[j][k]+sum[k]);
		}
	}
	printf("%d\n", sum[(1<<n)-1]-f[(1<<n)-1][n]);
	return 0;
}
posted @ 2021-08-08 16:10  Cyber_Tree  阅读(60)  评论(0编辑  收藏  举报