[CSP-S模拟测试]:旅行(数学+线段树)
题目传送门(内部题12)
输入格式
第一行,一个整数$n$,代表树的点数。
第二行,$n$个整数,第$i$个整数是$B_i$,描述排列$B$。
接下来$n−1$行,每行两个整数$u,v$,描述一条树边$(u,v)$。
保证$1\leqslant B_i\leqslant n$,$1\leqslant u\neq v\leqslant n$。保证数据合法。
输出格式
输出一个整数表示答案对${10}^9+7$取模的值。
样例
样例输入1:
5
2 1 3 5 4
1 2
2 3
2 4
4 5
样例输出1:
3
样例输入2:
6
6 4 5 3 2 1
1 2
2 3
3 4
4 5
5 6
样例输出2:
9
数据范围与提示
样例$1$解释:
满足条件的数列$A$分别是:
•$(1,2,3,4,5)$
•$(1,2,4,5,3)$
•$(2,1,3,4,5)$
数据范围:
对于所有数据,$1\leqslant n\leqslant 300,000$。
题解
首先,如果$dfs$的根小于$B[1]$,那么要求的就是以这个点为根的$dfs$序的个数,而对于一棵有根树,设其根为$i$,$dfs$序的个数为$f(i)$,则有:
$f(u)=|son(u)|!\prod \limits_{v\in son(u)}f(v)$
我们可以任选一个根求出$f$,然后计算其祖先贡献即可。
那么我们现在考虑以$B[1]$为根该怎么办?
思想和上述方法类似,即如果当前在$B_i$点,要走到$B_i+1$点,需要求出所有第$i+1$位小于$B_{i+1}$的方案数,简单计算即可。
为了通过最后两个点,可以用线段树或树状数组快速求出某个点下某棵子树的名次。
时间复杂度:$\Theta(n\log n)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
struct rec
{
int nxt;
int to;
}e[600000];
int head[300001],cnt;
int n;
int du[300001];
long long jc[300001],inv[300001],dp[300001],son[300001];
bool vis[300001],jump;
int rt[300001],b[300001],trval[1300000],trwzc[1300000],trsum[1300000],ls[1300000],rs[1300000],tot;
int dfn=1;
map<pair<int,int>,bool> mp;
long long ans,res=1,now;
void add(int x,int y)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
head[x]=cnt;
}
long long qpow(long long x,long long y)
{
long long res=1;
while(y)
{
if(y&1)res=res*x%1000000007;
x=x*x%1000000007;
y>>=1;
}
return res;
}
void pre_work()
{
jc[0]=inv[0]=1;
for(int i=1;i<=n;i++)
{
jc[i]=1LL*i*jc[i-1]%1000000007;
inv[i]=qpow(jc[i],1000000005);
}
}
void pushup(int x)
{
trsum[x]=trwzc[x];
if(ls[x])trsum[x]+=trsum[ls[x]];
if(rs[x])trsum[x]+=trsum[rs[x]];
}
void insert(int &x,int w)
{
if(!x)
{
x=++tot;
trval[x]=w;
trwzc[x]=trsum[x]=1;
return;
}
if(trval[x]>w)insert(ls[x],w);
else insert(rs[x],w);
pushup(x);
}
void change(int x,int w)
{
if(trval[x]==w)
{
trwzc[x]=0;
pushup(x);
return;
}
if(trval[x]>w)change(ls[x],w);
else change(rs[x],w);
pushup(x);
}
int find(int x,int w)
{
if(!x)return 0;
if(trval[x]==w)return trsum[ls[x]];
if(trval[x]>w)return find(ls[x],w);
return trsum[ls[x]]+trwzc[x]+find(rs[x],w);
}
void pre_dfs(int x)
{
dp[x]=1;
vis[x]=1;
for(int i=head[x];i;i=e[i].nxt)
if(!vis[e[i].to])
{
mp[make_pair(x,e[i].to)]=1;
insert(rt[x],e[i].to);
pre_dfs(e[i].to);
son[x]++;
dp[x]=dp[x]*dp[e[i].to]%1000000007;
}
dp[x]=dp[x]*jc[son[x]]%1000000007;
}
void pro_dfs(int x)
{
long long ez=son[x];
long long flag=0;
while(1)
{
if(!ez)break;
flag=find(rt[x],b[dfn+1]);
now=now*inv[ez]%1000000007*jc[ez-1]%1000000007;
ans=(ans+now*flag%1000000007)%1000000007;
if(mp[make_pair(x,b[dfn+1])])
{
dfn++;
ez--;
change(rt[x],b[dfn]);
pro_dfs(b[dfn]);
if(jump)break;
}else{jump=1;break;}
}
}
int main()
{
scanf("%d",&n);
pre_work();
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
du[x]++;
du[y]++;
}
long long res=jc[du[1]];
for(int i=2;i<=n;i++)
res=res*jc[du[i]-1]%1000000007;
if(b[1]!=1)ans=res;
for(int i=2;i<b[1];i++)
{
res=res*inv[du[i-1]]%1000000007*inv[du[i]-1]%1000000007*jc[du[i-1]-1]%1000000007*jc[du[i]]%1000000007;
ans=(ans+res)%1000000007;
}
pre_dfs(b[1]);
now=dp[b[1]];
pro_dfs(b[1]);
printf("%lld",ans);
return 0;
}
rp++
[CSP-S模拟测试]:旅行(数学+线段树)的更多相关文章
- [CSP-S模拟测试]:Weed(线段树)
题目描述 $duyege$的电脑上面已经长草了,经过辨认上面有金坷垃的痕迹.为了查出真相,$duyege$准备修好电脑之后再进行一次金坷垃的模拟实验.电脑上面有若干层金坷垃,每次只能在上面撒上一层高度 ...
- [CSP-S模拟测试]:光线追踪(线段树)
题目背景 初中时的乔猫试着组建了$NEWorld$开发组,可是不久之后却因为合作上的问题(和乔猫工程水平差,代码混乱的问题),开发组成员之间常常产生矛盾,关系越来越不如以前......一年下来,受到长 ...
- [CSP-S模拟测试]:椎(线段树维护区间最值和单调栈)
题目描述 虽不能至,心向往之. $Treap=Tree+Heap$ 椎$=$树$+$堆 小$\pi$学习了计算机科学中的数据结构$Treap$. 小$\pi$知道$Treap$指的是一种树. 小$\p ...
- [CSP-S模拟测试]:bird(线段树优化DP)
题目传送门(内部题89) 输入格式 第一行两个数$n$和$k$,分别表示小鸟的只数和$R$装弹时间.接下来$n$行,每行两个数$l,r$表示$n$只小鸟初始时的头和尾的$x$坐标. 输出格式 输出一个 ...
- [CSP-S模拟测试]:string(线段树)
题目描述 给定一个由小写字母组成的字符串$s$. 有$m$次操作,每次操作给定$3$个参数$l,r,x$. 如果$x=1$,将$s[l]~s[r]$升序排序: 如果$x=0$,将$s[l]~s[r]$ ...
- [CSP-S模拟测试]:Permutation(线段树+拓扑排序+贪心)
题目描述 你有一个长度为$n$的排列$P$与一个正整数$K$你可以进行如下操作若干次使得排列的字典序尽量小对于两个满足$|i−j|\geqslant K$且$|P_i−P_j|=1$的下标$i$与$j ...
- [CSP-S模拟测试]:最大值(数学+线段树)
题目背景 $Maxtir$最喜欢最大值. 题目传送门(内部题128) 输入格式 第$1$行输入四个正整数$n,m,q$. 第$2$至$n+1$行中,第$i+1$行输入魔法晶石$i$的三种属性$(x_i ...
- 【10.6校内测试】【小模拟】【hash+线段树维护覆盖序列】
一开始看到题就果断跳到T2了!!没想到T2才是个大坑,浪费了两个小时QAQ!! 就是一道小模拟,它怎么说就怎么走就好了! 为什么要用这么多感叹号!!因为统计答案要边走边统计!!如果每个数据都扫一遍20 ...
- Codeforces 280D k-Maximum Subsequence Sum [模拟费用流,线段树]
洛谷 Codeforces bzoj1,bzoj2 这可真是一道n倍经验题呢-- 思路 我首先想到了DP,然后矩阵,然后线段树,然后T飞-- 搜了题解之后发现是模拟费用流. 直接维护选k个子段时的最优 ...
随机推荐
- STM32 实现内部Flash的读写(HAL库版)
Flash 中文名字叫闪存,是一种长寿命的非易失性(断电数据不丢失)的存储器.可以对称为块的存储器单元块进行擦写和再编程,在进行写入操作之前必须先执行擦除.一个Nand Flash由多个块(Block ...
- mysql定时备份shell脚本
#!/bin/bash #每天早上4点, mysql备份数据 # backup.sh #crontab -e # * * * /home/erya/run/moniter/mysql_backup.s ...
- Fira Code,可以让不等号!=直接显示出来的字体
今天看B站某直播间有人写代码C#里一堆不等号直接显示,感觉很神奇,以为是插件还是什么新语法,托人问了下原来是Fira Code字体 https://github.com/tonsky/FiraCode ...
- css负边距布局
三行三列的布局 代码结构 <div class="container"> <ul> <li>1</li> <li>2&l ...
- Lambda -语法使用,代码简化
使用Lambda的方式实现线程 线程中()是run方法的(),可用来接受参数,格式: new Thread(()->{ System.out.println(Thread.currentThre ...
- 《JAVA设计模式》之单例模式(Singleton)
在阎宏博士的<JAVA与模式>一书中开头是这样描述单例模式的: 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 单例模式的 ...
- Java语言的特点与工作原理
Java语言的特点 1.简单性 Java语言与我们常听到的C++语言很像,但是没有C++那么繁琐.因为Java就是在C++之上设计出来的,设计者把C++的一些特性去掉了,这些特性在实际开发中,程序员也 ...
- BZOJ1672 Cleaning Shifts 清理牛棚
传送门 显然可以考虑 $dp$ 设 $f[i]$ 表示当前到了时间 $i$,从初始到 $i$ 的时间都安排好打扫了 把所有牛按照区间 $l,r$ 双关键字排序 这样枚举到一头牛 $x$ 时,在 $x. ...
- .net core 调用webservice同步方法
更新VS2019 16.1版本 支持WebService同步调用 在连接服务中->选择客户端选项->Generate Synchronout Operations选择划勾 生成同步操作 ...
- Docker Swanm集群配置
首先 可以用ContOS虚拟机 克隆 5个虚拟机,注意(克隆主机必须装了Docker,克隆后,克隆机都会有Docker) 配置 网络 克隆CentOS虚拟机 最后和到如下结果 打开2377端口 ...