noip模拟33

\(\color{white}{\mathbb{失足而坠千里,翻覆而没百足,名之以:深渊}}\)


1.png

这场考试的时间分配非常不科学

开题试图想 \(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{无可奈何花落去,似曾相识燕归来。}}\)

posted @ 2021-08-08 19:06  y_cx  阅读(59)  评论(2编辑  收藏  举报