20201014下午

20201014下午

虚树真是太潮啦!
请添加图片描述

T1T2T3
预测10040100
一测100068

T1:
究极缝合, e x g c d exgcd exgcd+ b s g s bsgs bsgs,不过是板子,打就完事了。
T2:
虚树不会啊,暴力还打挂了。
然后就去学习了一下虚树,可以在树形 d p dp dp中将有多次询问但总询问小于一定点数中将 n m nm nm复杂度降至 m l o g k mlogk mlogk k k k指总询问点数)。基本思想就是将树简化,建立一颗虚树只加入关键点与关键点的 l c a lca lca,用这些模拟整棵树进行 d p dp dp,具体实现通过栈来维护。
当前点 u u u,父边 w w w,本题显然有 d p dp dp为当 u u u为资源点时 f u = v w f_u=v_w fu=vw,否则 f u = min ⁡ ( v w , ∑ min ⁡ ( w ′ , f v ) ) f_u=\min(v_w,\sum\min(w',f_v)) fu=min(vw,min(w,fv))。那便记录 m n u mn_u mnu表示从 u u u 1 1 1路径的最小值,这样即使路径压缩了也能转移,然后用虚树即可解决。
贴个虚树的代码

bool cmp(int x,int y) //按dfn排序
{
	return dfn[x]<dfn[y];
}
void Insert(int v)
{
	int lca=LCA(v,sta[t]);
	if(lca==sta[t]) {sta[++t]=v;return;} //若当前点在子树内,直接入栈
	for(;t>1&&dfn[lca]<=dfn[sta[t-1]];t--) add2(sta[t-1],sta[t]); //说明已经不在子树内了,清理子树内节点
	if(lca!=sta[t]) add2(lca,sta[t]),sta[t]=lca; //若lca是新的节点,将栈顶替换
	sta[++t]=v;
	return;
}
void solve()
{
	cnt=0;sta[t=1]=1;sort(s+1,s+m+1,cmp); //记得每次初始化
	for(int i=1;i<=m;i++) Insert(s[i]),vis[s[i]]=true;
	for(;t>1;t--) add2(sta[t-1],sta[t]);
	printf("%lld\n",dp(1));
	for(int i=1;i<=m;i++) vis[s[i]]=false;
	return;
}

T3:
记忆化搜索,因为陷阱种类很少,所以可以对陷阱状态状压,对于一个陷阱有三种状态,毒陷阱,非毒陷阱,未知,所以需要三进制状压。用条件概率公式算出三进制中每个未知陷阱为有毒或无毒的概率,按这个搜就行。

#include<bits/stdc++.h>
using namespace std;
const int N=35,H=8,K=505;
const int fx[4]={1,0,-1,0},fy[4]={0,-1,0,1};
int n,m,k,h,sx,sy,p[1<<H],pow3[K];
bool vis[N][N][K][H];
double f[N][N][K][H],g[K][H];
char Map[N][N];
struct three{
	public:
		int x;
		three(int xx=0) {x=xx;return;}
		int get(int a) {return (x/pow3[a])%3;}
		three change(int a,int v) {return three(x-get(a)*pow3[a]+v*pow3[a]);}
}st;
void init()
{
	pow3[0]=1;
	for(int i=1;i<=k;i++) pow3[i]=pow3[i-1]*3;
	three S;bool flag;int tmp;
	for(;S.x<pow3[k];S.x++)
	{
		tmp=0;
		for(int i=0;i<(1<<k);i++)
		{
			flag=true;
			for(int j=0;j<k;j++) if(S.get(j)&&S.get(j)!=((i>>j)&1)+1) {flag=false;break;}
			if(!flag) continue;
			tmp+=p[i];
			for(int j=0;j<k;j++) if(!S.get(j)&&((i>>j)&1)) g[S.x][j]+=p[i];
		}
		for(int j=0;j<k;j++) if(!S.get(j)) g[S.x][j]/=1.0*tmp;
	}
	return;
}
bool in(int x,int y)
{
	if(x>=1&&x<=n&&y>=1&&y<=m) return true;
	return false;
}
double Dfs(int x,int y,three S,int hp)
{
	if(vis[x][y][S.x][hp]) return f[x][y][S.x][hp];
	vis[x][y][S.x][hp]=true;
	if(hp<=0) return f[x][y][S.x][hp]=0;
	if(Map[x][y]=='@') return f[x][y][S.x][hp]=1;
	for(int i=0;i<4;i++)
	{
		int tx=x+fx[i],ty=y+fy[i];
		if(Map[tx][ty]=='#'||!in(tx,ty)) continue;
		if(Map[tx][ty]<'A'||Map[tx][ty]>'Z') {f[x][y][S.x][hp]=max(f[x][y][S.x][hp],Dfs(tx,ty,S,hp));continue;}
		int tmp=Map[tx][ty]-'A';
		if(S.get(tmp)==1) f[x][y][S.x][hp]=max(f[x][y][S.x][hp],Dfs(tx,ty,S,hp));
		else if(S.get(tmp)==2) f[x][y][S.x][hp]=max(f[x][y][S.x][hp],Dfs(tx,ty,S,hp-1));
		else f[x][y][S.x][hp]=max(f[x][y][S.x][hp],g[S.x][tmp]*Dfs(tx,ty,S.change(tmp,2),hp-1)+(1-g[S.x][tmp])*Dfs(tx,ty,S.change(tmp,1),hp));
	}
	return f[x][y][S.x][hp];
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&k,&h);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",Map[i]+1);
		for(int j=1;j<=m;j++) if(Map[i][j]=='$') sx=i,sy=j;
	}
	for(int i=0;i<(1<<k);i++) scanf("%d",&p[i]);
	init();
	printf("%.3lf",Dfs(sx,sy,st,h));
	return 0;
}

然后你就过了 l u o g u luogu luogu l e m o n lemon lemon的数据,但 A O J AOJ AOJ怎么都过不去,怎么回事呢?
请添加图片描述
事实上上面那个算法是伪的,因为可能有环的情况出现,那么便会用一个未完全更新的状态更新导致答案错误。
正确算法应该是将走平地和已知无毒的陷阱处理掉,只将走到终点与有毒陷阱作为一步,先搜出来再搜一遍, 才能避免环的情况。。。
( A O J AOJ AOJ哪搞来的 h a c k hack hack数据,太离谱了。
总结: S C O I 2011 SCOI2011 SCOI2011一年两道错题,出数据的早该打打了。
请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值