【TC SRM 540 Div1】ProductQuery(区间DP)

传送门


题解:

首先根据中国剩余定理,我们知道 z % 2 = x , z % 5 = y z\%2=x,z\%5=y z%2=x,z%5=y z z z [ 0 , 9 ] [0,9] [0,9]有唯一解。

所以我们对于 % 2 \%2 %2 % 5 \%5 %5下求解然后乘法原理乘起来即可。

那么考虑怎么算合法方案,首先考虑没有 0 0 0的话每个限制都可以表示为两个前缀积之比。

那么没有 0 0 0的情况可以用带权并查集来判断是否合法,那么最后考虑自由位置的数量即可得到方案。

如果有 0 0 0怎么办。。。

考虑区间DP, g [ l ] [ r ] g[l][r] g[l][r]表示区间 [ l , r ] [l,r] [l,r]的答案,同时对于所有非零的限制,我们不会把它拆开。然后枚举这个区间第一个 0 0 0的位置,前面用非零的做法来算,后面的继续用 g g g计算。

实现直接记忆化搜索即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

cs int mod=1e9+7;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));return res;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

class ProductQuery{
	private:
		static cs int N=1e2+7;
		int inv[5]={0,1,3,2,4};
		int n,m,b,notzero[N];
		int f[N][N],g[N][N];
		struct atom{int l,r,v;};
		
		int fa[N],a[N];
		inline int gf(int u){
			if(fa[u]==u)return u;
			int tmp=gf(fa[u]);
			a[u]=a[u]*a[fa[u]]%b;
			return fa[u]=tmp;
		}
		inline bool merge(int u,int v,int val){
			if(!val)return false;
			int fu=gf(u),fv=gf(v);
			if(fu!=fv){
				fa[fu]=fv;
				a[fu]=a[v]*inv[a[u]]*inv[val]%b;
				return true;
			}
			else return a[v]*inv[a[u]]%b==val;
		}
		inline int F(int l,int r,cs std::vector<atom> &vec){//no zero
			if(!vec.size())return power(b-1,r-l+1);
			int &ans=f[l][r];if(~ans)return ans;
			for(int re i=l-1;i<=r;++i)fa[i]=i,a[i]=1;
			for(auto &t:vec)if(!merge(t.l-1,t.r,t.v))return ans=0;
			int ct=0;for(int re i=l-1;i<=r;++i)if(fa[i]==i)++ct;
			return ans=power(b-1,ct-1);
		}
		
		inline int G(int l,int r,cs std::vector<atom> &vec){
			if(!vec.size())return power(b,r-l+1);
			int &ans=g[l][r];if(~ans)return ans;
			ans=F(l,r,vec);
			for(int re i=l;i<=r;++i)if(!notzero[i]){
				bool flag=true;
				std::vector<atom> v1,v2;
				for(auto &t:vec){
					if(t.r<i){
						if(t.v==0){flag=false;break;}
						v1.push_back(t);
					}
					if(t.l>i)v2.push_back(t);
				}
				if(flag)Inc(ans,mul(F(l,i-1,v1),G(i+1,r,v2)));
			}
			return ans;
		}
		
		inline int solve(cs std::vector<atom> &vec){
			memset(notzero,0,sizeof notzero);
			for(auto &t:vec){
				int l=t.l,r=t.r,v=t.v;
				if(v)for(int re i=l;i<=r;++i)notzero[i]=true;
			}
			for(auto &t:vec){
				int l=t.l,r=t.r,v=t.v;
				if(!v){
					bool flag=true;
					for(int re i=l;i<=r;++i)flag&=notzero[i];
					if(flag)return 0;
				}
			}
			memset(f,-1,sizeof f);
			memset(g,-1,sizeof g);
			return G(1,n,vec);
		}
	public:
		ProductQuery(){}
		int theInput(int N,std::vector<int> ql,
			std::vector<int> qr,std::vector<int> ans){
			n=N,m=ql.size();	
			b=2;int res=1;
			std::vector<atom> vec;
			for(int re i=0;i<m;++i)
			vec.push_back((atom){ql[i]+1,qr[i]+1,ans[i]&1});
			Mul(res,solve(vec));b=5;
			for(int re i=0;i<m;++i)vec[i].v=ans[i]%5;
			return mul(res,solve(vec));
		}
};

#ifdef zxyoi

ProductQuery Solver;

signed main(){
	std::cout<<Solver.theInput(
58,
{5, 5},
{8, 8},
{1, 2}
	)<<"\n";
	return 0;
}

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值