hdu6044
题意:给出每个点是某段区间的最大值,问构造一个1到n的排列,有多少种符合要求的。
做法:用笛卡尔树,如果对于一个序列,最大值可以把序列分成两段,左边一段和右边一段,然后在对这两段进行分割,,,最大值连接左右子段的最大值,就可以得到一颗树,这棵树有两个性质,左边的点比根的下标小,有点的点比跟的下标大。跟的值是子树中最大的,这就刚好符合笛卡尔树的性质,然后就可以利用题目中给出的信息构造笛卡尔树,如果没有合法的树,那么答案就是0,否者1:可以用排列的方法求出答案,对于一个子树,跟部肯定要选择最大的树,然后把剩下的点选出一点放到左边,选出一些放到右边,方案树就是comb(区间长度-1,左区间长度),然后乘以左右子树的构造方案树,就是这个子树的方案了,递归o(n)的。
2:如果题目中的数据可以构造出一颗笛卡尔树,那么答案就是n!/(每个节点的儿子个数),n个数可以构造n!个不同序列,那么对于某个子树来说,合法的方案概率是这个子树的节点数的倒数,那么每个节点都符合的概率就是所有节点它儿子个数的乘积的倒数。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;
int l[N],r[N];
int ls[N],rs[N],root;
int n;
int f[N],inv[N],invf[N];
int que[N];
const int mod = 1e9+7;
bool judge(int x,int y){
return r[y]-l[y]-r[x]+l[x] > 0;
}
namespace IO {
const int MX = 4e7; //1e7 占用内存 11000kb
char buf[MX]; int c, sz;
void begin() {
c = 0;
sz = fread(buf, 1, MX, stdin);//一次性全部读入
/*int tot = 1;
char now;
while((now = getchar()) != EOF){
buf[tot++] = now;
}
sz = tot;*/
}
inline bool read(int &t) {
while (c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;
if (c >= sz) return false;//若读完整个缓冲块则退出
bool flag = 0; if(buf[c] == '-') flag = 1, c++;
for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0';
if(flag) t = -t;
return true;
}
}
void build(){
int tp = 0;
root =0;
for(int i = 1;i <= n;i ++){
ls[i] = rs[i] = 0;
while(tp && judge(que[tp],i)) tp --;
if(tp == 0){
ls[i] = root;
root = i;
}
else{
ls[i] = rs[que[tp]];
rs[que[tp]] = i;
rs[i] =0;
}
que[++tp] = i;
}
}
void init(){
inv[1] = 1;
for(int i= 2;i < N;i ++){
inv[i] = (mod-mod/i)*1LL*inv[mod%i]%mod;
}
f[0] = invf[0] = 1;
for(int i= 1;i < N;i ++){
f[i] = f[i-1]*1LL*i%mod;
invf[i] = invf[i-1]*1LL*inv[i]%mod;
}
}
int comb(int x,int y){
return 1LL*f[x]*invf[y]%mod*invf[x-y]%mod;
}
long long dfs(int x,int nl,int nr){
if(x == 0){
return 1;
}
if(l[x] != nl ||r[x] != nr) return 0;
long long op = comb(nr-nl+1,r[x]-l[x]+1);
op = op*comb(nr-nl,x-nl)%mod;
op = op*dfs(ls[x],nl,x-1)%mod;
op = op*dfs(rs[x],x+1,nr)%mod;
//cout << x<< ' '<<nl << ' '<<nr << ' '<<op << endl;
return op;
}
void print(int x){
if(x == 0) return ;
printf("%d %d %d\n",x,ls[x],rs[x]);
print(ls[x]);
print(rs[x]);
}
int main(){
init();
IO::begin();
int kase = 1;
//cout <<comb(3,3)<<endl;
while(IO::read(n)){
for(int i = 1;i <= n;i ++) IO::read(l[i]);
for(int i = 1;i <= n;i ++) IO::read(r[i]);
build();
//print(root);
//cout<<"!!!!!" << endl;
long long ans = dfs(root,1,n);
printf("Case #%d: %lld\n",kase++,ans);
}
return 0;
}
hdu 6305
题意:给出一个数组a,b数组的元素是0到1任意的,问a,b笛卡尔树同构的方案的期望。
根据a数组构造笛卡尔树,可以知道同构的概率是所有节点的儿子树乘积的倒数,可以发现对于任意一种序列,b数组为这个序列的期望是一样的,因为总期望是n/2,所以同构的期望是上面那个概率乘以n/2,注意会爆栈。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;
int num[N];
int ls[N],rs[N],root;
int que[N];
int son[N];
int inv[N];
int ss[N];
int fa[N];
int n;
const int mod = 1e9+7;
bool judge(int x,int y){
if(num[x] == num[y]) return x > y;
return num[x] < num[y];
}
void build(){
int tp = 0;
root = 0;
for(int i = 1;i <= n;i ++){
ls[i] = rs[i] = 0;
while(tp && judge(que[tp],i)) tp --;
//cout << que[tp] << ' ' << i << ' '<<root << endl;
if(tp == 0){
ls[i] = root;
root = i;
}
else{
ls[i] = rs[que[tp]];
rs[que[tp]] = i;
}
que[++tp] = i;
}
}
/*void dfs(int x){
//cout <<x << ' ' << ls[x] << ' ' <<rs[x] << endl;
son[x] = 0;
if(x == 0) return ;
son[x] = 1;
dfs(ls[x]);
dfs(rs[x]);
son[x] += son[ls[x]];
son[x] += son[rs[x]];
}*/
void init(){
inv[1] = 1;
for(int i = 2;i < N;i ++){
inv[i] = (mod-mod/i)*1LL*inv[mod%i]%mod;
}
}
int main(){
int T;
init();
cin >> T;
while(T--){
scanf("%d",&n);
for(int i = 1;i <= n;i ++) scanf("%d",&num[i]);
//for(int i= 1;i <= n;i ++) num[i] = i;
build();
//memset(ss,0,sizeof(ss));
for(int i = 1;i <= n;i ++){
ss[i] = 0;
if(ls[i]) ss[i]++,fa[ls[i]] = i;
if(rs[i]) ss[i]++,fa[rs[i]] = i;
}
fa[root] = 0;
queue<int> que;
for(int i = 1;i <= n;i ++){
son[i] =1;
if(ss[i] == 0) que.push(i);
}
while(!que.empty()){
int now = que.front();
//cout <<now << ' '<<fa[now] << ' '<<son[now] << endl;
que.pop();
if(fa[now] == 0) continue;
ss[fa[now]] --;
son[fa[now]] += son[now];
if(ss[fa[now]] == 0) que.push(fa[now]);
}
//cout<<"!!" << endl;
long long ret = n;
for(int i = 1;i <= n;i ++){
//cout <<i << ' '<<son[i] << ' '<< inv[son[i]] << endl;
ret= ret*inv[son[i]]%mod;
}
ret = ret*inv[2]%mod;
printf("%lld\n",ret);
}
return 0;
}