noip模拟33
\(\color{white}{\mathbb{失足而坠千里,翻覆而没百足,名之以:深渊}}\)
这场考试的时间分配非常不科学
开题试图想 \(t1\) 正解,一个半小时后还是只有暴力,特别惊慌失措
然后赶紧看 \(t2\),看题发现是个简单的线段树合并,没有多模样例,半个小时打完结论后发现能过样例,也没对拍就直接放下了
然后最后一个小时硬想 \(t3\),写了一个复杂度比较正确的网络流上去,发现有好多漏洞,然后一直调,最后考试结束的时候甚至暴力都没来得及打
A. Hunter
玄妙的概率题
如果从第几个猎人在第几轮死的概率考虑,发现很难维护,因为当前值的总和未知
一个很妙的方法是利用成比例的性质,可以计算出每一个猎人比第一个先死的概率,即 \(\displaystyle\frac{w_i}{w_1+w_i}\),那么总和+1即为第一个猎人死的期望
B. Defence
线段树合并的裸题,注意答案是 \(min(lmax+rmax,maxx)\)
还要注意每个点的法术可能有多个
C. Connect
数据范围特别小,一看是状压
题意可以理解为选取一条1到n的链,所有已选点集最多和链上的一个点相连
\(f[S][i]\) 表示已选集合为 \(S\),链上最后一个点是 \(i\) 的已选边权和的最大值
那么每次可以新加入一个点,延长链的长度
也可以新增一个集合,作为链旁边的点集
转移即可,最后用总边权减去即可
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=20,maxm=5e4+5;
const int inf=0xcfcfcfcf;
int n,m,cnt,hd[maxn],x,y,w,val[maxn][maxn],tot,limit,f[maxm][maxn],ans,sum[maxm],con[maxm][maxn],sta[maxn],tp;
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
struct Edge{
int nxt,from,to,val;
}edge[maxm];
void add(int u,int v,int w){
edge[++cnt].nxt=hd[u];
edge[cnt].to=v;
edge[cnt].from=u;
edge[cnt].val=w;
hd[u]=cnt;
return ;
}
void calc(int x){
tp=0;
while(x)sta[++tp]=(x%2),x>>=1;
for(int i=n+1;i>=1;i--)cout<<sta[i];
return ;
}
int main(){
n=read();
// calc(1);
// cout<<endl;
// calc(6),cout<<endl;
m=read();
for(int i=1;i<=m;i++){
x=read();
y=read();
w=read();
add(x,y,w);
add(y,x,w);
tot+=w;
val[x][y]=val[y][x]=w;
}
limit=(1<<n)-1;
for(int S=1;S<=limit;S++){
for(int i=1;i<=cnt;i++){
int u=edge[i].from;
int v=edge[i].to;
if((S&(1<<(u-1)))&&(S&(1<<(v-1))))sum[S]+=edge[i].val;
if((S&(1<<(u-1)))&&(!(S&(1<<(v-1)))))con[S][v]+=edge[i].val;
}
sum[S]/=2;
}
memset(f,0xcf,sizeof f);
f[1][1]=0;
for(int S=1;S<=limit;S++){
// calc(S);
// cout<<endl;
for(int i=1;i<=n;i++){
if(f[S][i]==inf)continue;
for(int j=hd[i];j;j=edge[j].nxt){
int v=edge[j].to;
if(S&(1<<(v-1)))continue;
// if(con[S^(1<<(i-1))][v])continue;
f[S|(1<<(v-1))][v]=max(f[S|(1<<(v-1))][v],f[S][i]+edge[j].val);
}
int SS=limit^S;
// if(S==1&&i==1)calc(SS),cout<<endl;
for(int T=SS;T;T=(T-1)&SS){
// if(S==1&&i==1)cout<<"ppp "<<T<<" ",calc(T),cout<<endl;
f[S|T][i]=max(f[S|T][i],f[S][i]+sum[T]+con[T][i]);
}
}
}
cout<<sum[limit]-f[limit][n];
return 0;
}
\(\color{white}{\mathbb{无可奈何花落去,似曾相识燕归来。}}\)