zoukankan      html  css  js  c++  java
  • 【2019.10.7 CCF-CSP-2019模拟赛 T3】未知的数组(unknown)(并查集+动态规划)

    预处理

    考虑模数(10)是合数不好做,所以我们可以用一个常用套路:

    (prod_{i=l}^ra_iequiv x(mod 10))的方案数等于(prod_{i=l}^ra_iequiv x(mod 2))的方案数乘上(prod_{i=l}^ra_iequiv x(mod 5))的方案数。

    状态设置

    考虑接下来怎么求。

    既然现在模数是质数,而在模质数意义下的逆元是唯一的,除了(0)没有逆元,因此只要特殊考虑(0)

    (f_{i,j})表示 将区间([i,j])填满且满足此区间内所有条件并不能填(0)的方案数(g_{i,j})表示 将区间([i,j])填满且满足此区间内所有条件但可以填(0)的方案数

    那么答案就是(g_{1,n})

    如何求(g_{l,r})

    首先,(g_{l,r})可以从(f_{l,r})转移得到。

    然后,考虑枚举一个(p),表示([l,r])中最左边的(0)的位置。

    显然(p)要满足条件中不存在一个包含(p)的区间积不为(0)

    (g_{l,r})就可以加上(f_{l,p-1} imes g_{p+1,r})

    因为(p)是第一个(0),所以左边不能有(0),右边可以有(0)

    如何求(f_{l,r})

    因为不能有(0),所以我们可以设(s_i)表示(prod_{x=l}^ia_x),即前缀积。

    对于一组条件例如([x,y])区间内的积为(v),则可以得到(a_{x-1}^{-1} imes a_y=v)

    我们可以用带权并查集存储这种关系,同时检验是否合法。

    当所有条件处理完后,假设有(t)个连通块,当前的模数是(V),由于不能填(0),所以方案数是((V-1)^{t-1})

    (f_{l,r}=(V-1)^{t-1})

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    #define Q 50
    #define X 1000000007
    using namespace std;
    int n,Qt;
    struct Query
    {
    	int l,r,v;I Query(CI x=0,CI y=0,CI p=0):l(x),r(y),v(p){}
    	I Query operator % (CI x) Con {return Query(l,r,v%x);}
    }s[Q+5];
    class DpSolver
    {
    	private:
    		#define VQ vector<Query>
    		#define IT VQ::iterator
    		#define pb push_back
    		int V,Inv[5],f[N+5][N+5],g[N+5][N+5],fa[N+5],vl[N+5];VQ p;
    		I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}//快速幂
    		I int getfa(CI x)//找祖先,路径压缩同时更新边权
    		{
    			if(fa[x]==x) return x;RI t=fa[x];fa[x]=getfa(fa[x]),vl[x]=vl[x]*vl[t]%V;
    			return fa[x];
    		}
    		I bool Union(CI x,CI y,CI v)//合并,若已连通判断是否合法
    		{
    			RI fx=getfa(x),fy=getfa(y);if(fx==fy) return vl[y]*Inv[vl[x]]%V==v;//判断是否合法
    			return fa[fx]=fy,vl[fx]=vl[y]*Inv[vl[x]]%V*Inv[v]%V,true;//合并
    		}
    		I int F(CI l,CI r,VQ v)//求f[l][r],v表示[l,r]内的条件
    		{
    			if(v.empty()) return Qpow(V-1,r-l+1);if(~f[l][r]) return f[l][r];//若无条件随便填,若已求过直接返回
    			RI i,t=0;IT it;for(i=l-1;i<=r;++i) fa[i]=i,vl[i]=1;//并查集初始化
    			for(it=v.begin();it!=v.end();++it)//迭代器遍历vector
    				if(!it->v||!Union(it->l-1,it->r,it->v)) return f[l][r]=0;//若不合法返回0
    			for(i=l-1;i<=r;++i) i==fa[i]&&++t;return f[l][r]=Qpow(V-1,t-1);//统计连通块个数计算答案
    		}
    		I int G(CI l,CI r,VQ v)//求g[l][r],v表示[l,r]内的条件
    		{
    			if(v.empty()) return Qpow(V,r-l+1);if(~g[l][r]) return g[l][r];//若无条件随便填,若已求过直接返回
    			RI i,fg;VQ v1,v2;IT it;for(g[l][r]=F(l,r,v),i=l;i<=r;++i)//枚举第一个0所在位置
    			{
    				for(fg=1,v1.clear(),v2.clear(),it=v.begin();it!=v.end();++it)//迭代器遍历vector
    				{
    					if(it->l<=i&&i<=it->r&&it->v) {fg=0;break;}//如果一个包含当前位置的区间积不为0,就不合法
    					it->r<i&&(v1.pb(*it),0),it->l>i&&(v2.pb(*it),0);//将条件分成两部分
    				}fg&&(g[l][r]=(1LL*F(l,i-1,v1)*G(i+1,r,v2)+g[l][r])%X);//更新g值
    			}return g[l][r];//返回答案
    		}
    	public:
    		I void Solve()
    		{
    			#define Init(x) (V=x,p.clear(),memset(f,-1,sizeof(f)),memset(g,-1,sizeof(g)))
    			RI i,t;for(Init(2),Inv[1]=1,i=1;i<=Qt;++i) p.pb(s[i]%2);t=G(1,n,p);//模数为2
    			for(Init(5),Inv[1]=1,Inv[2]=3,Inv[3]=2,Inv[4]=4,i=1;i<=Qt;++i) p.pb(s[i]%5);//模数为5
    			printf("%d",1LL*t*G(1,n,p)%X);//方案数相乘
    		}
    }S;
    int main()
    {
    	freopen("unknown.in","r",stdin),freopen("unknown.out","w",stdout);
    	RI i;for(scanf("%d%d",&n,&Qt),i=1;i<=Qt;++i)
    		scanf("%d%d%d",&s[i].l,&s[i].r,&s[i].v),++s[i].l,++s[i].r;//读入,出于习惯将下标加1
    	return S.Solve(),0;
    }
    
  • 相关阅读:
    Create, Read, Write, Copy, Move and Delete a Text File using C#
    财富人生访谈
    aspnetdb数据库简介
    asp.net基于Profile和Provider技术实现购物车
    IIS7的FTP设置
    DataRow[]用作DataSource找不到列的问题
    SMTP 550错误
    简单的文件上传代码
    选取哪个字段作为分区依据
    IIS7的虚拟目录设置独立应用程序池
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Contest20191007T3.html
Copyright © 2011-2022 走看看