JZOJ 5913 林下风气
题目
问一个树中有多少个连通块最大最小点权之差等于 k k k
分析
也就是
(
<
=
k
)
的
个
数
−
(
<
=
k
−
1
)
的
个
数
(<=k)的个数-(<=k-1)的个数
(<=k)的个数−(<=k−1)的个数,那么就可以枚举点,对于这个点求的是以它为最大点权符合要求的个数,那么容易得到合法条件
a
[
m
a
x
]
≥
a
[
s
o
n
]
且
a
[
m
a
x
]
−
a
[
s
o
n
]
≤
m
且
s
o
n
>
m
a
x
(
避
免
点
权
重
复
)
或
者
a
[
m
x
]
<
>
a
[
s
o
n
]
a[max]\geq a[son]且a[max]-a[son]\leq m且son>max(避免点权重复)或者a[mx]<>a[son]
a[max]≥a[son]且a[max]−a[son]≤m且son>max(避免点权重复)或者a[mx]<>a[son]
根据乘法原理得到
d
p
(
m
a
x
)
=
∏
i
=
s
o
n
∈
m
a
x
d
p
(
i
)
+
1
dp(max)=\prod _{i=son\in max}^{dp(i)+1}
dp(max)=i=son∈max∏dp(i)+1
代码
#include <cstdio>
#define rr register
using namespace std;
const int mod=19260817;
struct node{int y,next;}e[6701];
int k=1,ls[3401],a[3401],n,ans,m;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
inline void add(int x,int y){
e[++k]=(node){y,ls[x]}; ls[x]=k;
e[++k]=(node){x,ls[y]}; ls[y]=k;
}
inline signed dp(int x,int fa,int mx){
rr long long ans=1;
for (rr int i=ls[x];i;i=e[i].next){
if (e[i].y==fa||a[mx]<a[e[i].y]) continue;
if (a[mx]-a[e[i].y]<=m&&(e[i].y>mx||a[mx]!=a[e[i].y])) ans=ans*(dp(e[i].y,x,mx)+1)%mod;
}
return ans;
}
signed main(){
freopen("lkf.in","r",stdin);
freopen("lkf.out","w",stdout);
n=iut(); m=iut();
for (rr int i=1;i<=n;++i) a[i]=iut();
for (rr int i=1;i<n;++i) add(iut(),iut());
for (rr int i=1;i<=n;++i) ans=(ans+dp(i,0,i))%mod;//小于等于k
if (m--) for (rr int i=1;i<=n;++i) ans=(ans-dp(i,0,i))%mod;//小于k
printf("%d",(ans+mod)%mod);
return 0;
}
JZOJ 5194 盟主的忧虑
题目
在一棵树上删掉一条边后找一条边权最小的边使其恢复删掉的边的连通性
分析
也就是说多余的边的两点所经过的边是被多余的边的边权贡献的,所以说可以用并查集维护
代码
#include <cstdio>
#include <queue>
#include <algorithm>
#define rr register
#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
const int N=100001;
struct node{int x,y,w,next;}e[N*3];
int n,m,f[N+1],k,pre[N+1],ans[N+1],ls[N+1],dep[N+1],father[N+1];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
inline signed add(int x,int y,int w){
e[++k]=(node){x,y,w,ls[x]}; ls[x]=k;
e[++k]=(node){y,x,w,ls[y]}; ls[y]=k;
}
signed cmp(node a,node b){return a.w<b.w;}
inline void print(int ans){
if (ans<0) putchar('-'),ans=-ans;
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void bfs(){
rr queue<int>q1,q2;
q1.push(1); q2.push(0);
while (q1.size()){
rr int x=q1.front(),y=q2.front(); q1.pop(); q2.pop();
father[x]=y,dep[x]=dep[y]+1;
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].y!=y) pre[e[i].y]=e[i].w,q1.push(e[i].y),q2.push(x);
}
}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline void doit(int &x,int y){ans[pre[x]]=y,f[x]=getf(father[x]),x=f[x];//跳到父节点上}
signed main(){
freopen("worry.in","r",stdin);
freopen("worry.out","w",stdout);
n=iut(); k=m=iut(); f[n]=n;
for (rr int i=1;i<n;++i) add(iut(),iut(),i),f[i]=i,ans[i]=-1;
for (rr int i=1;i<=m;++i) e[i]=(node){iut(),iut(),iut()};
sort(e+1,e+1+m,cmp); bfs();//从小到大排序
for (rr int i=1;i<=m;++i){
rr int fa=getf(e[i].x),fb=getf(e[i].y);
while (fa!=fb) doit(dep[fa]<dep[fb]?fb:fa,e[i].w);//找深度大的点往上跳
}
for (rr int i=1;i<n;++i) print(ans[i]),putchar(10);
return 0;
}
JZOJ 5907 轻功
题目
从起点0到终点 n n n,可以选择花费 w i w_i wi秒经过 c i c_i ci个木桩,但其间不能有限制,更换 i i i需要 t t t的时间,第1次选择不需要时间,问最少的时间
分析
那么就是动态规划了
f
[
n
]
[
k
]
f[n][k]
f[n][k]表示到达点
n
n
n并使用过第
k
k
k种方式的最少时间
f
[
x
+
c
[
i
]
]
[
i
]
=
m
i
n
{
f
[
x
]
[
j
]
+
w
[
i
]
}
f[x+c[i]][i]=min\{f[x][j]+w[i]\}
f[x+c[i]][i]=min{f[x][j]+w[i]}
初始化
f
[
c
[
i
]
]
[
i
]
=
w
[
i
]
f[c[i]][i]=w[i]
f[c[i]][i]=w[i]
最后答案
m
i
n
{
f
[
n
]
[
i
]
}
min\{f[n][i]\}
min{f[n][i]}
代码
#include <cstdio>
#include <queue>
#define rr register
#define r(i,a,b) for (rr int i=a;i<=b;++i)
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
int n,k,tim,c[101],w[101],s[501][101],minn=2147483647; long long ans,f[501][101];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
signed main(){
freopen("qinggong.in","r",stdin);
freopen("qinggong.out","w",stdout);
n=iut(); k=iut(); tim=iut(); rr int minx=2147483647;
r(i,1,k) c[i]=iut(),w[i]=iut(),minx=min(minx,c[i]);
for (rr int t=iut();t;--t) s[iut()][iut()]=1; ans=1e18;
r(j,1,k) r(i,1,n) s[i][j]+=s[i-1][j],f[i][j]=1e18;
r(i,1,k) if (s[c[i]][i]<=0) f[c[i]][i]=w[i],minn=min(c[i],minn);
r(x,minn,n-minx) r(i,1,k) if (x+c[i]<=n) r(j,1,k)
if (s[x+c[i]][i]-s[x-1][i]<=0&&f[x][j]!=1e18)//前缀和没有限制
f[x+c[i]][i]=min(f[x+c[i]][i],f[x][j]+w[i]+(i!=j)*tim);
r(i,1,k) ans=min(ans,f[n][i]);
if (ans<1e18) printf("%lld",ans); else printf("-1");
return 0;
}