noip模拟33


1.png

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

开题试图想 t1 正解,一个半小时后还是只有暴力,特别惊慌失措
然后赶紧看 t2,看题发现是个简单的线段树合并,没有多模样例,半个小时打完结论后发现能过样例,也没对拍就直接放下了
然后最后一个小时硬想 t3,写了一个复杂度比较正确的网络流上去,发现有好多漏洞,然后一直调,最后考试结束的时候甚至暴力都没来得及打


A. Hunter

玄妙的概率题
如果从第几个猎人在第几轮死的概率考虑,发现很难维护,因为当前值的总和未知

一个很妙的方法是利用成比例的性质,可以计算出每一个猎人比第一个先死的概率,即 wiw1+wi,那么总和+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;
}

posted @   y_cx  阅读(74)  评论(2编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示