2022年天梯赛-全国总决赛 L3-2 关于深度优先搜索和逆序对的题应该不会很难吧这件事 (30 分)
作者:互联网
2022天梯赛L3-2
前情提要
一个\([1,2,3,4,5,...,n]\)的全排列逆序对和顺序对数目相同
例如:\([1,2,3]\)
他的全排列方案有:
[1,2,3] 逆序对 0 顺序对 3
[1,3,2] 逆序对 1 顺序对 2
[2,1,3] 逆序对 2 顺序对 1
[2,3,1] 逆序对 2 顺序对 1
[3,1,2] 逆序对 1 顺序对 2
[3,2,1] 逆序对 3 顺序对 0
所以一个全排列的逆序对有\(n!/2\)种
分析
1.首先一个树的dfs序的所有排列方式计算方式
每一个父亲节点有多少儿子节点的阶乘。
例如样例:\(2!*2!\)
2.如何计算他的祖先们有多少比他大的数
例如:
\(2\) 这个节点,这棵树排列怎么变化,3和5永远在他前面,所以我们可以用树状数组统计出有多少比他的数,再乘上这棵树的排列方式(具体看代码实现)
3.在不同子树中如何计算有多少逆序对
这一步也是这个题的关键
对于一个父节点,他有很多子树,每一次统计这些子树之间有多少逆序对
例如样例:
对于3这个结点
他有两个子树
[4],[5,1,2]
先统计这些区间全排列之间有多少逆序对
根据前情提要全排列逆序对的数目是全排列的一般
所以这一段逆序对的数目2!*(其他父节点的儿子节点的方案是)/2
所以还是这颗树的方案/2,我们已经通过分析1搞出来了
但这样的方案的数目是少的,因为上面分析的每个区间是1,但现在每个区间是他们的size,所以要再乘他们两两之间size的乘积
ans=全排列的方案
所以对于3这个节点,他的子树之间ans/2*siz[4]*siz[5]
代码实现
/*made in mrd*/
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10,mod=1e9+7;
#define lowbit(x) x&-x
#define endl '\n'
#define int long long
#define mem(a,b) memset(a,b,sizeof a)
#define fi first
#define se second
#define lu u<<1
#define ru u<<1|1
#define pb push_back
#define bug1(x) cout<<x<<endl
#define bug2(x,y) cout<<x<<' '<<y<<endl
#define pii pair<int,int>
#define bug3(x,y,z) cout<<x<<' '<<y<<' '<<z<<endl
void init()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
}
vector<int>g[N];
int siz[N];
int num[N];
int res=1;
int fact[N], infact[N];
int sum=0;
int qsm(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int C(int m, int n) {//组合数
if (n<0||m<0||m<n) return 0;
return fact[m] * infact[m - n] % mod * infact[n] % mod;
}
void cinit() {//预处理阶乘
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++) fact[i] = fact[i - 1] * i % mod;
infact[N - 1] = qsm(fact[N - 1], mod - 2);
for (int i = N - 2; i; i --) infact[i] = infact[i + 1] * (i + 1) % mod;
}
void dfs(int u,int fa)//统计size和算出这棵树的方案
{
int cnt=0;
for(int i=0;i<g[u].size();i++)
{
int j=g[u][i];
if(j==fa) continue;
dfs(j,u);
siz[u]+=siz[j];
cnt++;
}
siz[u]+=1;
res=res*fact[cnt]%mod;
}
int tr[N];//树状数组统计他的父亲有多少比他大的
void add(int x,int y) {
for(int i=x;i<N;i+=lowbit(i)) tr[i]+=y;
}
int ask(int x) {
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
void dfs1(int u,int fa)
{
sum+=(ask(N-10)-ask(u))*res;//树状数组统计他的父亲有多少比他大的
sum%=mod;
add(u,1);
int pre=0;
int ans=0;
for(int i=0;i<g[u].size();i++)
{
int j=g[u][i];
if(j==fa) continue;
ans=ans+pre*siz[j];
pre+=siz[j];
ans%=mod;
}
sum+=ans*res%mod*qsm(2,mod-2)%mod;//在不同子树中如何计算有多少逆序对
for(int i=0;i<g[u].size();i++){
int j=g[u][i];
if(j==fa) continue;
dfs1(j,u);
}
add(u,-1);
}
signed main() {
init();
cinit();
int n,root;
cin>>n>>root;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
g[u].pb(v);
g[v].pb(u);
}
dfs(root,0);
//cout<<res<<endl;
dfs1(root,0);
cout<<sum<<endl;
return 0;
}
标签:排列,int,res,30,L3,2022,逆序,节点,define 来源: https://www.cnblogs.com/mrd-T/p/16191361.html