xiaosaitmp
title: xiaosaitmp
categories:
- ICPC
tags:
- null
abbrlink: 2b0f3b8d
date: 2024-09-03 00:00:00
题面翻译
给你一个$N$个节点的树,求一个$1\cdots N$的排列$(p_1,p_2,\cdots p_N)$ ,使得$\sum dist(i,p_i)$最大。
求这样的排列的个数。答案对$10^9+7$取模。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int>ttfa;
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||'9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
const int N=100005;
const ll MOD=1000000007;
int n,root,siz[N],fiz[N];
ll fac[N],ifc[N],dp[N],ans;
inline ll C(ll n,ll r){return fac[n]*ifc[r]%MOD*ifc[n-r]%MOD;}
vector<int>edge[N];
inline ll fpr(ll b,ll t=MOD-2,ll x=1){
for(;t;t>>=1,b=b*b%MOD)
if(t&1)x=x*b%MOD;
return x;
}
void dfs(int u,int f){
siz[u]=1,fiz[u]=0;
for(auto v:edge[u]){
if(v==f)continue;
dfs(v,u);
siz[u]+=siz[v];
fiz[u]=max(fiz[u],siz[v]);
}fiz[u]=max(fiz[u],n-siz[u]);
if(fiz[u]<fiz[root])root=u;
}
int main(){
n=read();
fac[0]=ifc[0]=1;
for(int i=1;i<=n;++i)fac[i]=fac[i-1]*i%MOD;
ifc[n]=fpr(fac[n]);
for(int i=n-1;i>=1;--i)ifc[i]=ifc[i+1]*(i+1)%MOD;
for(int i=1;i<n;++i){
int u=read(),v=read();
edge[u].push_back(v);
edge[v].push_back(u);
}
fiz[root=0]=n;dfs(1,0);dfs(root,0);
dp[0]=1ll;
for(auto v:edge[root]){
int s=siz[v];
for(int i=n;i>=0;--i){//背包
for(int j=1;j<=min(i,s);++j){
ll tmp=C(s,j)*C(s,j)%MOD*fac[j]%MOD;
dp[i]=(dp[i]+dp[i-j]*tmp%MOD)%MOD;
}
}
}
for(int i=0;i<=n;++i)
ans=(ans+((i&1)?-1ll:1ll)*dp[i]*fac[n-i]%MOD+MOD)%MOD;
printf("%lld\n",ans);
return 0;
}
只求最大值的网络赛代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
# define int long long
#define ull unsigned long long
#define pii pair<int,int>
#define baoliu(x, y) cout << fixed << setprecision(y) << x
#define endl "\n"
#define alls(x) (x).begin(), (x).end()
#define fs first
#define sec second
const int N = 1e6 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-8;
const double PI = acos(-1.0);
int n, m;
int a[N];
int ans=0;
struct edge{
int v,w;
};
vector<edge>e[N];
int sz[N];
void dfs1(int u,int fa){
sz[u]=1;
for(auto [v,w]:e[u]){
if(v==fa)continue;
dfs1(v,u);
sz[u]+=sz[v];
}
}
void dfs2(int u,int fa){
for(auto [v,w]:e[u]){
if(v==fa)continue;
ans+=2*w*min(sz[v],n-sz[v]);
dfs2(v,u);
}
}
void solve(){
cin>>n;
for(int i=1;i<=n-1;i++){
int u,v,w;cin>>u>>v>>w;
e[u].push_back({v,w});
e[v].push_back({u,w});
}
dfs1(1,0);
dfs2(1,0);
cout<<ans<<endl;
}
signed main() {
cin.tie(0);
ios::sync_with_stdio(false);
int t=1;
//cin>>t;
while (t--) {
solve();
}
return 0;
}
题目大意
有一个n个节点的树(编号1~n)。树上的每一个边都是正值。
我们定义两点之间的距离$d(v,u)$为这两点最短路径边的权值之和。
设数列$p$为一个1~n的全排列。求使得$\sum_{i=1}^n d(i,p_i)$最大的、字典序列最小的全排列$p$。
输入格式
第一行是一个正整数 $n (1 \le n \le 10^5) $
接下来n-1行是三个整数$ u_{i},v_{i},w_{i} (1<=u_{i},v_{i}<=n; 1<=w_{i}<=10^{5}) $,分别代表$ u_{i}$到$v_{i}$有一条权值为$w_i$的路径
数据保证这是一棵树
输出格式
第一行是所求的最大和
第二行是所求的字典序列最小的全排列
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
const int N=100005;set<int>nd[N],id;set<pair<int,int> >dd;int nid=0;
struct edge{int to,w,nxt;}e[N<<1];int et,head[N],d[N],p[N];
int n,sz[N],rt,dfn[N],nfd[N],dt,f[N],tag[N];ll rs=0;
inline void adde(int x,int y,int w) {e[++et]=(edge){y,w,head[x]},head[x]=et;}
inline void getrt(int x,int fa)
{
sz[x]=1;int mx=0;for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
getrt(e[i].to,x),sz[x]+=sz[e[i].to],mx=max(mx,sz[e[i].to]);
mx=max(mx,n-sz[x]);if(mx<=n/2) rt=x;
}
inline void dfs0(int x,int fa)
{
dfn[x]=++dt,nfd[dt]=x,sz[x]=1,f[x]=fa;
for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
dfs0(e[i].to,x),sz[x]+=sz[e[i].to],rs+=2ll*min(sz[e[i].to],n-sz[e[i].to])*e[i].w;
}
int main()
{
read(n);for(int i=1,x,y,w;i<n;i++) read(x,y,w),adde(x,y,w),adde(y,x,w);
if(n==1) return puts("0\n1\n"),0;else if(n==2) return printf("%lld\n2 1\n",2ll*e[head[1]].w),0;
getrt(1,0),dfs0(rt,0),printf("%lld\n",rs);
for(int i=head[rt],x;i;i=e[i].nxt)
{
++nid,x=e[i].to;for(int l=dfn[x];l<dfn[x]+sz[x];l++) nd[tag[nfd[l]]=nid].insert(nfd[l]);
id.insert(*nd[nid].begin()),dd.insert(make_pair(d[nid]=n-2*nd[nid].size(),nid));
}
nd[tag[rt]=++nid].insert(rt),dd.insert(make_pair(d[nid]=n-2,nid)),id.insert(rt);
int tg=0;for(int i=1;i<=n;i++)
{
int ps=tag[i],tw;dd.erase(make_pair(d[ps],ps));
if(dd.begin()->first+tg==0&&dd.begin()->second!=tag[rt]) tw=dd.begin()->second;
else if(tag[*id.begin()]==ps&&i!=rt) tw=tag[*++id.begin()];else tw=tag[*id.begin()];
int qw=*nd[tw].begin();p[i]=qw,tg--,dd.erase(make_pair(d[tw],tw));
id.erase(qw),nd[tw].erase(nd[tw].begin());if(!nd[tw].empty()) id.insert(*nd[tw].begin());
dd.insert(make_pair(++d[ps],ps)),dd.insert(make_pair(++d[tw],tw));
}
for(int i=1;i<=n;i++) printf("%d%c",p[i],i==n?'\n':' ');
return 0;
}
[ABC160F] Distributing Integers
题面翻译
有一颗节点编号为$1$至$N$的树,第$i$条边连接点$a_i$和$b_i$。对于$1$至$N$的每个$k$进行如下操作$:$
- 按如下操作在树上每个点写一个数字$:$
- 在点$k$上写上$1$
- 按从$2$到$N$的顺序将数写在节点上$:$
- 选择一个仍未写有数字且与已写有数字的点相邻的点,如果有多个这样的点,随机选择一个。
- 输出所有写法的数量(结果模$10^9+7$)
就是从叶子开始求拓扑序的数量
#include <bits/stdc++.h>
using namespace std;
const int MAXN=200010,P=1e9+7;
int n,x,y,eg,hd[MAXN],ver[2*MAXN],nx[2*MAXN],siz[MAXN],f[MAXN];
void add_edge (int x,int y) {
ver[++eg]=y;
nx[eg]=hd[x];
hd[x]=eg;
return;
}
int qpow (int a,int b) {
int res=1;
while (b) {
if (b&1) {res=(1ll*res*a)%P;}
a=(1ll*a*a)%P,b>>=1;
}
return res;
}
void dfs1 (int x,int fa) {
siz[x]=1;
for (int i=hd[x];i;i=nx[i]) {
if (ver[i]==fa) {continue;}
dfs1(ver[i],x);
siz[x]+=siz[ver[i]];
}
f[1]=(1ll*f[1]*siz[x])%P;
return;
}
void dfs2 (int x,int fa) {
for (int i=hd[x];i;i=nx[i]) {
if (ver[i]==fa) {continue;}
f[ver[i]]=(1ll*f[x]*((1ll*(n-siz[ver[i]])*qpow(siz[ver[i]],P-2))%P))%P;
dfs2(ver[i],x);
}
return;
}
int main () {
scanf("%d",&n);
f[1]=1;
for (int i=1;i<=n-1;i++) {
scanf("%d%d",&x,&y);
add_edge(x,y),add_edge(y,x);
}
dfs1(1,0);
dfs2(1,0);
int tmp=1;
for (int i=1;i<=n;i++) {tmp=(1ll*tmp*i)%P;}
for (int i=1;i<=n;i++) {printf("%d\n",(1ll*tmp*qpow(f[i],P-2))%P);}
return 0;
}
**题意:求多个有根树构成的森林的拓扑序方案数 对10^9 + 7取模
*思路:每棵子树的序号转换成从 1 开始不重复的递增序号*,*加上一个超级源点 0 将其转化为求单棵有根树的拓扑序方案数
#include<bits/stdc++.h>
using namespace std;
#define __T int csT;scanf("%d",&csT);while(csT--)
#define endl '\n'
const int mod=1e9+7;
const double PI= acos(-1);
const double eps=1e-6;
const int N=2e5+7;
int n,c,q,u,cnt;
struct node{
int nx,to;
}e[100033];//记得多开几条边用于超级源点与每棵树根结点的连边
int h[100003];
long long dp[100003],jc[100003];
long long qpow(long long a,long long x)
{
long long d=a,sum=1;
while(x>0)
{
if(x%2==1)
{
sum=(sum*d)%mod;
}
x>>=1;
d=(d*d)%mod;
}
return sum;
}
long long inv(long long x)
{
return qpow(x,mod-2);
}
int sz[100003];
void szdfs(int at)
{
sz[at]=1;
for(int i=h[at];~i;i=e[i].nx)
{
int to=e[i].to;
szdfs(to);
sz[at]+=sz[to];
}
}
void dpdfs(int at)
{
dp[at]=jc[sz[at]-1];
for(int i=h[at];~i;i=e[i].nx)
{
int to=e[i].to;
dpdfs(to);
dp[at]=(dp[at]*dp[to]%mod*inv(jc[sz[to]]))%mod;
}
}
inline void sol()
{
scanf("%d",&n);
memset(h,-1,sizeof h);
memset(sz,0,sizeof sz);
q=cnt=0;
for(int i=1;i<=n;++i)
{
scanf("%d",&c);
e[++cnt].to=q+1;
e[cnt].nx=h[0];
h[0]=cnt;
for(int j=2;j<=c;++j)
{
scanf("%d",&u);
u=q+u;
e[++cnt].to=q+j;
e[cnt].nx=h[u];
h[u]=cnt;
}
q+=c;
}
jc[0]=1;
jc[1]=1;
for(int i=2;i<=q;++i)jc[i]=(jc[i-1]*i)%mod;//预处理好模意义下的阶乘
szdfs(0);//先求出每个结点的 size
dpdfs(0);
printf("%lld\n",dp[0]);
}
int main()
{
sol();
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 爱飞鱼的blog!