把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷7024】[NWRRC2017] Fygon 2.0(缩点+状压DP)

题目链接

  • 给定一个仅由 \(q\)for 语句和一个 lag 语句构成的程序。
  • for 语句可以描述为条件 \(\alpha\le i\le \beta(i\in \mathbb{N^*})\),其中 \(i\) 是这个 for 语句对应的变量名,\(\alpha\) 为一个先前定义的变量或 \(1\)\(\beta\) 为一个先前定义的变量或 \(n\)
  • 定义 \(f(n)\)lag 语句的执行次数,求非负整数 \(k,C\) 满足 \(\lim_{n\rightarrow\infty}\frac{f(n)}{C\cdot n^k}=1\)
  • \(q\le 20\)

建图+缩点

对于一对不等关系 \(\alpha\le i\le \beta\),可以连出 \(\alpha\rightarrow i\)\(i\rightarrow\beta\) 两条有向边(不用对 1n 建图)。

如果若干个点之间的边构成了环,那么这个环中的点必须全部相等。所以我们用 Tarjan 缩点,然后得到一张 DAG。

由于 \(n\) 可以视作一个很大的数,而 DAG 中这些点之间的约束又相对宽泛,故 DAG 中的点数就是第一个答案 \(k\) 了。

状压 DP

现在要求系数 \(C\)。因为可以假设所有点取值各不相等,拓扑序数量为 \(res\)(即变量的相对大小可能情况数),容易得到 \(C=\frac{res}{k!}\)

我们先在 DAG 上拓扑对每个点求出拓扑序必须在它前面的点集。

然后考虑状压 DP,每次枚举一个点尝试加入,要求必须在他前面的点集已经全部出现。

这样一来就能计算出 \(res\),进而就能得出 \(C\) 了。

代码:\(O(q2^q)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define L 20
#define LL long long
using namespace std;
string s;int ex[30];LL f[1<<L];I LL gcd(Cn LL& x,Cn LL& y) {return y?gcd(y,x%y):x;}
struct Graph
{
	int ee,lnk[30];struct edge {int to,nxt;}e[2*L+5];I void Add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y;}
	I int operator [] (CI x) Cn {return lnk[x];}I edge operator () (CI x) {return e[x];}
}G,nG; 
int d,dfn[30],low[30],T,S[30],IS[30],ct,bl[30];I void Tarjan(CI x)//缩点
{
	dfn[x]=low[x]=++d,IS[S[++T]=x]=1;for(RI i=G[x],y;i;i=G(i).nxt)
		dfn[y=G(i).to]?IS[y]&&(low[x]=min(low[x],dfn[y])):(Tarjan(y),low[x]=min(low[x],low[y]));
	if(dfn[x]==low[x]) {++ct;W(bl[S[T]]=ct,IS[S[T]]=0,S[T--]^x);}
}
int q[30],D[30],o[1<<L];I void Topo()//拓扑
{
	RI i,k,H=1,T=0;for(k=1;k<=ct;++k) for(i=nG[k];i;i=nG(i).nxt) ++D[nG(i).to];for(k=1;k<=ct;++k) !D[k]&&(q[++T]=k);
	W(H<=T) for(i=nG[k=q[H++]];i;i=nG(i).nxt) o[nG(i).to]|=o[k]|1<<k-1,!--D[nG(i).to]&&(q[++T]=nG(i).to);//对每个点求出必须在它前面的点集
}
I void Calc()//计算答案
{
	RI i,j;LL t=1;for(i=1;i<=26;++i) ex[i]&&!dfn[i]&&(Tarjan(i),0);//Tarjan
	for(i=1;i<=26;++i) if(ex[i]) for(j=G[i];j;j=G(j).nxt) bl[i]^bl[G(j).to]&&(nG.Add(bl[i],bl[G(j).to]),0);Topo();//建新图然后拓扑
	RI l=1<<ct;for(f[i=0]=1;i^l;++i) for(j=1;j<=ct;++j) !(i>>j-1&1)&&(i&o[j])==o[j]&&(f[i|(1<<j-1)]+=f[i]);//状压DP
	LL fc=1;for(i=1;i<=ct;++i) fc*=i;LL g=gcd(f[l-1],fc);printf("%d %lld/%lld\n",ct,f[l-1]/g,fc/g);
}
int main()
{
	RI Tt=0,i,x;for(getline(cin,s),i=0,x=s.length();i^x&&isdigit(s[i]);++i) (Tt*=10)+=s[i]&15;
	for(i=0;i<=Tt;++i) if(getline(cin,s),s[4*i]=='l') return Calc(),0;
		else ex[x=s[4*i+4]&31]=1,s[4*i+15]!='1'&&(G.Add(s[4*i+15]&31,x),0),s[4*i+18]!='n'&&(G.Add(x,s[4*i+18]&31),0);//根据不等关系建图
}
posted @ 2021-11-25 18:50  TheLostWeak  阅读(87)  评论(0编辑  收藏  举报