T1
解析:
排列组合+树形DP。
令
f
[
i
]
f[i]
f[i]表示以
i
i
i为根的子树的合法种数,就有:
f
[
x
]
=
C
s
i
z
e
[
x
]
−
1
−
s
i
z
e
[
y
1
]
s
i
z
e
[
y
1
]
∗
f
[
y
1
]
∗
.
.
.
.
.
.
C
s
i
z
e
[
x
]
−
1
−
s
i
z
e
[
y
1
]
−
s
i
z
e
[
y
2
]
−
.
.
.
.
.
.
s
i
z
e
[
y
n
]
s
i
z
e
[
y
1
]
∗
f
[
y
n
]
f[x]=C_{size[x]-1-size[y_1]}^{size[y_1]}*f[y_1]*......C_{size[x]-1-size[y_1]-size[y_2]-......size[y_n]}^{size[y_1]}*f[y_n]
f[x]=Csize[x]−1−size[y1]size[y1]∗f[y1]∗......Csize[x]−1−size[y1]−size[y2]−......size[yn]size[y1]∗f[yn]
意思就是每个分配给子树的方案数乘以这个子树的合法方案个数(感性理解)
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int Max=100010;
int n,m,s;
int size[Max],first[Max],f[Max],mul[Max],inv[Max];
struct shu{int to,next;}edge[Max<<1];
inline int get_int()
{
int x=0,f=1;char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y)
{
edge[++s].next=first[x],first[x]=s,edge[s].to=y;
edge[++s].next=first[y],first[y]=s,edge[s].to=x;
}
inline int mu(int a,int b){return a*b>=mod?a*b%mod:a*b;}
inline int ksm(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=mu(ans,a);
b>>=1,a=mu(a,a);
}
return ans;
}
inline int C(int n,int m)
{
if(n>=0&&m>=0&&n>=m) return mu(mul[n],mu(inv[m],inv[n-m]));
return 0;
}
inline void dfs(int p,int fa)
{
f[p]=1;
for(int u=first[p];u;u=edge[u].next)
{
int to=edge[u].to;
if(to==fa) continue;
dfs(to,p);
f[p]=mu(mu(f[p],f[to]),C(size[p]+size[to],size[to]));
size[p]+=size[to];
}
size[p]++;
}
signed main()
{
n=get_int();
mul[0]=1,inv[0]=1;
for(int i=1;i<=n;i++) mul[i]=mu(mul[i-1],i),inv[i]=ksm(mul[i],mod-2);
for(int i=1;i<n;i++)
{
int x=get_int(),y=get_int();
build(x,y);
}
dfs(1,0);
cout<<f[1];
return 0;
}
T2
解析:
分治。
表示不想看单调栈的做法,况且分治速度吊打单调栈啊。
很多区间计数类问题都可以用分治解决,这道题因为直接算不好算,考虑用总数减去不合法数,又因为满足
f
[
i
]
≤
  
g
[
i
]
f[i]\leq\;g[i]
f[i]≤g[i],所以我们只用算出满足
f
[
i
]
=
g
[
i
]
f[i]=g[i]
f[i]=g[i]的区间的个数即可。
实现:
- 对于当前区间,取
m
i
d
mid
mid,枚举左端点,对于每个满足
m
a
x
{
n
u
m
[
i
]
.
.
.
n
u
m
[
m
i
d
]
}
=
n
u
m
[
i
∣
∣
.
.
.
∣
n
u
m
[
m
i
d
]
max\{num[i]...num[mid]\}=num[i||...|num[mid]
max{num[i]...num[mid]}=num[i∣∣...∣num[mid]
的数,计算右端点最多能延申到哪。枚举右端点同理。 - 递归 ( l , m i d ) , ( m i d + 1 , r ) (l,mid),(mid+1,r) (l,mid),(mid+1,r)求解。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=1000010;
int n,m;
long long ans;
int num[Max],f[Max],g[Max];
inline int get_int()
{
int x=0,f=1;char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48);
return x*f;
}
inline void init()
{
n=get_int();
for(int i=1;i<=n;i++) num[i]=get_int();
ans=(long long)n*(n-1)/2;
}
inline void solve(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
f[mid]=g[mid]=num[mid];
for(register int i=mid-1;i>=l;i--) f[i]=max(num[i],f[i+1]),g[i]=num[i]|g[i+1];
f[mid+1]=g[mid+1]=num[mid+1];
for(register int i=mid+2;i<=r;i++) f[i]=max(num[i],f[i-1]),g[i]=num[i]|g[i-1];
int pos=mid;
for(register int i=mid;i>=l;i--)
{
if(g[i]!=f[i]) continue;
while(pos<r&&(g[pos+1]|g[i])==g[i]&&f[pos+1]<=f[i]) pos++;
ans-=pos-mid;
}
pos=mid+1;
for(register int i=mid+1;i<=r;i++)
{
if(g[i]!=f[i]) continue;
while(pos>l&&(g[pos-1]|g[i])==g[i]&&f[pos-1]<f[i]) pos--; //‘<’防止重复,仔细体会
ans-=mid-pos+1;
}
solve(l,mid),solve(mid+1,r);
}
int main()
{
init();
solve(1,n);
cout<<ans;
return 0;
}
T3
解析:
最短路。
先考虑这样建图。对于与每个点相连的边,枚举入边和出边,将入边和出点看成点,连接一条长度为
m
a
x
(
l
e
n
1
,
l
e
n
2
)
max(len_1,len_2)
max(len1,len2)的单向边。跑最短路。
思考这样建图是
O
(
M
2
)
O(M^2)
O(M2)的,考虑怎样优化。对于一个点,先把出入边按权值排序,入边连向他的反向边,权值为这条边原本的权值,每个反向边往下连长度为
0
0
0的边,向上连长度为两边差值的边。
画个图就大概长这样:
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=200010;
int n,m,size=1,tot;
long long ans=1e18,dis[Max<<1];
int first[Max];
bool vis[Max<<1];
struct shu{int to,next,len;}edge[Max<<1];
struct in{int id,val;}e[Max<<1];
vector<pair<int,int> >edg[Max<<1];
inline int get_int()
{
int x=0,f=1;char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y,int z)
{
edge[++size].next=first[x],first[x]=size,edge[size].len=z;
edge[++size].next=first[y],first[y]=size,edge[size].len=z;
}
inline bool comp(const in &a,const in &b){return a.val<b.val;}
inline void dijkstra()
{
for(int i=2;i<=size;i++) dis[i]=1e18;
priority_queue<pair<long long,int> >q;
for(int u=first[1];u;u=edge[u].next) dis[u]=edge[u].len,q.push(make_pair(-edge[u].len,u));
while(q.size())
{
int p=q.top().second;q.pop();
if(vis[p]) continue;vis[p]=1;
for(int j=0;j<edg[p].size();j++)
{
int to=edg[p][j].second;
if(dis[to]>dis[p]+edg[p][j].first)
{
dis[to]=dis[p]+edg[p][j].first;
q.push(make_pair(-dis[to],to));
}
}
}
}
inline void init()
{
n=get_int(),m=get_int();
for(int i=1;i<=m;i++)
{
int x=get_int(),y=get_int(),z=get_int();
build(x,y,z);
}
}
inline void pre()
{
for(int i=2;i<n;i++)
{
tot=0;
for(int u=first[i];u;u=edge[u].next) e[++tot].id=u,e[tot].val=edge[u].len;
sort(e+1,e+tot+1,comp);
for(int i=1;i<=tot;i++)
{
if(i!=1) edg[e[i].id].push_back(make_pair(0,e[i-1].id));
if(i!=tot) edg[e[i].id].push_back(make_pair(e[i+1].val-e[i].val,e[i+1].id));
edg[e[i].id^1].push_back(make_pair(e[i].val,e[i].id));
}
}
}
inline void solve()
{
dijkstra();
for(int u=first[n];u;u=edge[u].next)
{
int to=edge[u].to;
ans=min(ans,dis[u^1]+edge[u].len);
}
cout<<ans;
}
int main()
{
init();
pre();
solve();
return 0;
}