2018年10月30日提高组

99 篇文章 1 订阅
47 篇文章 0 订阅

JZOJ 5913 林下风气

题目

问一个树中有多少个连通块最大最小点权之差等于 k k k


分析

也就是 ( &lt; = k ) 的 个 数 − ( &lt; = k − 1 ) 的 个 数 (&lt;=k)的个数-(&lt;=k-1)的个数 (<=k)(<=k1),那么就可以枚举点,对于这个点求的是以它为最大点权符合要求的个数,那么容易得到合法条件 a [ m a x ] ≥ a [ s o n ] 且 a [ m a x ] − a [ s o n ] ≤ m 且 s o n &gt; m a x ( 避 免 点 权 重 复 ) 或 者 a [ m x ] &lt; &gt; a [ s o n ] a[max]\geq a[son]且a[max]-a[son]\leq m且son&gt;max(避免点权重复)或者a[mx]&lt;&gt;a[son] a[max]a[son]a[max]a[son]mson>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=sonmaxdp(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值