[NWRRC2017]Fygon 2.0

280 篇文章 1 订阅
122 篇文章 0 订阅

题目

传送门 to luogu

思路

刚拿到这道题,确实没有什么很好的思路。然后 H a n d I n D e v i l \sf HandInDevil HandInDevil 就把它秒切了……

事实上,循环变量 j j j [ l , r ] [l,r] [l,r] 范围内枚举,等价于 对于任意 j ∈ [ 1 , n ] j\in[1,n] j[1,n] 的情况,仅 l ⩽ j ⩽ r l\leqslant j\leqslant r ljr 时统计循环次数。因为 1 ⩽ l ∧ r ⩽ n 1\leqslant l\wedge r\leqslant n 1lrn 嘛。

于是考虑建出大小关系的有向图, a ⩽ b a\leqslant b ab a a a b b b 连边。此时强连通应当取同一个值,缩点后是 D A G \rm DAG DAG 。看到 n ⩽ 20 n\leqslant 20 n20,是不是心中有想法了呢?

还是不会诶……感觉转移比较困难。没想到这东西可以更抽象一点:渐进复杂度确实是 n → + ∞ n\rightarrow+\infty n+ 的取值。那么 f ( n ) n k    ( n → + ∞ ) \frac{f(n)}{n^k}\;(n\rightarrow+\infty) nkf(n)(n+) 的含义是什么?有 k k k 个在 [ 1 , n ] [1,n] [1,n] 以内随机取值的变量,要求它满足 D A G \rm DAG DAG 所限定的大小关系的概率。

对于任意一种特定的大小关系 x 1 < x 2 < ⋯ < x k x_1<x_2<\cdots<x_k x1<x2<<xk,其概率显然是 1 k ! \frac{1}{k!} k!1 。而自变量取值相等的情况,在 n → + ∞ n\rightarrow+\infty n+ 时,概率 1 n → 0 + \frac{1}{n}\rightarrow0^+ n10+,可以直接 忽略不计

那么怎么计算所有可行的大小关系呢?其实就是求不同的拓扑排序结果,状压 d p \tt dp dp 就好了!时间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)

代码

为了不写 tarjan 我进行了一些骚操作……

#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; '0'>c||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
void writeUnsigned(const unsigned &x){
	if(x > 9) writeUnsigned(x/10);
	putchar((x-x/10*10)^48);
}
inline void writeint(const int &x){
	if(x >= 0) writeUnsigned(x);
	else putchar('-'), writeUnsigned(-x);
}
inline void getMin(int &a,const int &b){ if(b < a) a = b; }
inline void getMax(int &a,const int &b){ if(a < b) a = b; }
inline int_ getGcd(int_ a,int_ b){
	for(; b; a%=b,swap(a,b));
	return a;
}

const int MAXN = 20;
int g[1<<MAXN];
bool scc[1<<MAXN];
int bel[MAXN]; /// belong to which @c scc
int f[MAXN][MAXN]; /// if two nodes are NOT connected

int_ dp[1<<MAXN], all;
int haxi[255];
char str[MAXN];
bool used[1<<MAXN];
int main(){
	int n = readint()-1;
	rep(i,0,n-1) fill(f[i],f[i]+n,1);
	rep(i,0,n-1){
		scanf("%*s %s",str);
		haxi[int(*str)] = i;
		scanf("%*s %s",str); // range(L,
		if(str[6] != '1'){
			const int &x = haxi[int(str[6])];
			g[1<<x] |= (1<<i), f[x][i] = 0;
		}
		scanf("%s",str); // R):
		if(*str != 'n'){
			const int &x = haxi[int(*str)];
			g[1<<i] |= (1<<x), f[i][x] = 0;
		}
	}
	rep(i,0,n-1) f[i][i] = 0; // self-loop
	rep(k,0,n-1) rep(i,0,n-1) rep(j,0,n-1)
		getMin(f[i][j],f[i][k]+f[k][j]);
	for(int S=1; S<(1<<n); ++S){
		g[S] = g[S&(-S)]|g[S&(S-1)];
		scc[S] = true; // check scc
		rep(i,0,n-1) if(S>>i&1){
			rep(j,i+1,n-1) if(S>>j&1)
				if(f[i][j] || f[j][i])
					scc[S] = false;
			break; // one node is enough
		}
		if(scc[S] == false) continue;
		rep(i,0,n-1) if(S>>i&1) bel[i] = S;
	}
	{ // calculate exponent
		int ddg = 0;
		rep(i,0,n-1){
			if(!used[bel[i]]) ++ ddg;
			used[bel[i]] = true;
		}
		printf("%d ",ddg);
		rep(i,0,n-1) used[bel[i]] = false;
		rep(i,int(all=1),ddg) all *= i;
	}
	dp[0] = 1; // no node deleted
	for(int S=0; S<(1<<n)-1; ++S){
		if(!dp[S]) continue;
		rep(i,0,n-1) if(!(S>>i&1)){
			int x = bel[i]; // whole scc
			if(used[x]) continue;
			used[x] = true; // visited
			if((g[x]&(S^x)) == g[x])
				dp[S^x] += dp[S];
		}
		rep(i,0,n-1)
			used[bel[i]] = false;
	}
	int_ d = getGcd(all,dp[(1<<n)-1]);
	printf("%lld/%lld\n",dp[(1<<n)-1]/d,all/d);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值