某 SCOI 模拟赛 T2 走(walk)【状压DP 剪枝】

题意

n n n 个点排成一个环,你从某个点开始,每步操作从等概率选择四个操作之一:移到左边第二个点、 移到左边第一个点、移到右边第二个点、移到右边第一个点。当你走到任何一个点两次时你会立刻停止行走。 求操作的期望步数。答案模 p p p n ≤ 80 n\leq 80 n80

题解

首先有 2 n 2^n 2n 的状压 DP,记 1 为到过的点。

接着假如环上有至少两个连续的 11,与当前位置被 11 隔开的地方肯定无法到达。于是把这些 11 之间的位置设成 1,这样状态数变少了,就跑过去了。

(小问号你是否有许多朋友?) \text{(小问号你是否有许多朋友?)} (小问号你是否有许多朋友?)

std 的 DP 方式与 DP 顺序似乎更高一些,可以在 DP 途中在 map 里删元素使得 map 变小速度变快;我的解法得用 unordered map 才跑得过去。

代码:

#include<bits/stdc++.h>
using namespace std;
int getint(){
	int ans=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		ans=ans*10+c-'0';
		c=getchar();
	}
	return ans;
}
#define ll long long
#define bs bitset<80>
int n,mod;
int inv2,inv4;
struct stat{
	int pos;
	bitset<80>a;
	bool gg(){
		return a[pos];
	}
	stat go(int del){
		stat b;
		b.a=a;b.a.set(pos);
		b.pos=(pos+del+n)%n;
		return b;
	}
	bool cut(){
		#define P(x) ((x)+1<n?(x)+1:0)
		for(int i=P(pos);i!=pos;i=P(i)){
			if(a[i]&&a[P(i)]){
				int j=P(i),r=j;
				for(;j!=pos;j=P(j)){
					if(a[j]&&a[P(j)])r=j;
				}
				for(j=P(i);j!=r;j=P(j))a[j]=1;
				return 1;
			}
		}
		return 0;
	}
};
bool operator== (const stat &a,const stat &b){
	return a.pos==b.pos&&hash<bs>()(a.a)==hash<bs>()(b.a);
}
struct qaq{
	const size_t operator() (const stat &a)const noexcept{
		return hash<bs>()(a.a)^(a.pos<<5);
	}
};
ostream& operator<< (ostream &out,const stat &b){
	out<<b.a.to_string()<<"["<<b.pos<<"]";
	return out;
}
unordered_map<stat,ll,qaq>f;
ll calc(stat s){
	//cerr<<"calc "<<s<<endl;
	if(s.gg())return 0;
	bool b=s.cut();
	auto it=f.find(s);
	if(it!=f.end()){
		ll ans=it->second;
		//if(!b)f.erase(it);
		return ans;
	}
	it=f.insert(make_pair(s,
		(4ll+calc(s.go(1))+calc(s.go(2))+calc(s.go(-1))+calc(s.go(-2)))*inv4%mod)).first;
	//cerr<<it->first<<" "<<it->second<<endl;
	return it->second;
}

int main(){
	n=getint(),mod=getint();
	inv2=(mod+1)>>1;
	inv4=inv2*1ll*inv2%mod;
	cout<<calc(stat({0,bs()}));
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值