恩偶爱皮模拟尸体-33
水博客太快乐了。。。。
烤场
先看了三题,发现 \(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;
}