P7618

Problem

\(n\) 个嵌套 for 循环,对每个循环变量给出上下界(数或至多一个外层的变量),求最内层的循环次数。


Solution

通过日常代码经验,交换两个毫无关联(没有直接或间接关联)的循环对整体次数没有影响,所以,我们考虑对每一坨关联起来的循环分开计算。

对于一坨循环,通过它们的关联关系,把它们画成一棵树,表示其依赖关系。

然后,掏出树形 DP,\(f_{u,i}\) 表示循环 \(u\) 的循环变量为 \(i\) 时其子树的总循环次数,对于 \(u\) 的每个儿子 \(v_k\)它们的先后是随意的。所以,为了便于理解,假设先进行 \(v_1\) 的所有循环,再进行 \(v_2,v_3,\cdots,v_k\) 的循环。设当儿子的循环变量取 \(l\) 时可以转移到当前节点,于是可以得到(分步乘起来):

\[f_{u,i}=\prod\limits_{j=1}^{k}\sum f_{v_j,l} \]

只剩最后一个问题,儿子中有哪些 \(l\) 可以转移过来。这是比较容易的,对于一个已经确定的循环变量 \(i\),可以根据上下界的定义,直接 for 模拟一遍。

简单计算一下时间复杂度为 \(O(n\times10^{10})\)\(n\) 个循环,一个循环值域为 \(10^5\),再枚举一遍儿子节点的值域 \(10^5\))。明显是不行的。

再研究一下,显而易见地发现,儿子中可转移的东西是连续的(毕竟是 ++i),于是就拿出前缀和优化,砍掉一个 \(10^5\)

最后,在算完每一坨之后,因为是嵌套的循环,就把每一坨的循环次数乘起来得出答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N(30),M(1e5+10),mod(1e9+7);
int n,a[N],b[N],d[N],h[N],ne[N],e[N],idx;
ll f[N][M],ans=1,sum[N][M];
bool fg[N];
inline ll mo(ll x){
	return x<mod?x:x-mod;
}
inline void add(int u,int v){
	ne[++idx]=h[u],h[u]=idx,e[idx]=v;
}
inline void dfs(int u){
	for(int i=a[u];i<=b[u];++i) f[u][i]=1;
	for(int i=h[u];i;i=ne[i]){
		int v=e[i];
		dfs(v);
		for(int j=a[u];j<=b[u];++j){
			if(d[v]) f[u][j]=f[u][j]*(a[v]<=j?sum[v][j]-sum[v][a[v]-1]+mod:0)%mod;
			else f[u][j]=f[u][j]*(j<=b[v]?sum[v][b[v]]-sum[v][j-1]+mod:0)%mod;
		}
	}
	for(int i=a[u];i<=b[u];++i) sum[u][i]=mo(sum[u][i-1]+f[u][i]);
}
int main(){
	string l,r;
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		a[i]=1,b[i]=M-1;
		cin>>l>>r;
		int ff=0;
		if(l[0]>='a'&&l[0]<='z') add(l[0]-'a'+1,i);
		else{
			a[i]=0;
			for(int j=0;j<l.size();++j) a[i]=(a[i]<<1)+(a[i]<<3)+(l[j]-'0');
			++ff;
		}
		if(r[0]>='a'&&r[0]<='z') add(r[0]-'a'+1,i),d[i]=1;
		else{
			b[i]=0;
			for(int j=0;j<r.size();++j) b[i]=(b[i]<<1)+(b[i]<<3)+(r[j]-'0');
			++ff;
		}
		if(ff==2) fg[i]=1;
	}
	for(int i=1;i<=n;++i)
		if(fg[i]) dfs(i),ans=ans*(sum[i][b[i]]-sum[i][a[i]-1]+mod)%mod;
	printf("%lld",ans);
	return 0;
}
posted @ 2023-11-12 13:12  mRXxy0o0  阅读(62)  评论(0)    收藏  举报