1113比赛总结

1 抓球

题面不说了,就是一道普通的概率DP,可惜我求范围的时候没有看清xi,yi的巨大范围,暴力求概率,结果炸裂了。所以教训是,教训之一是数据范围不一定是从小到大的范围来的,其二是不能只看N,M等的范围。

X连通块计数
时间限制 : - MS   空间限制 : - KB 
评测说明 : 1s,128m
问题描述

给出一棵n个点的树,每个点有一个权值ai。从这棵树上选出一个点集,使得选出的点连通,且满足点集中的点的权值最大值与最小值之差不超过k,问有多少种选点集的办法。
两种选点集的办法不同当且仅当点集中的点的标号不同。

输入格式

第一行,包含两个整数n,k。
第二行,包含n个整数a1, a2, · · · , an。
接下来n − 1行,每行包含两个正整数u, v,表示u, v两点间有一条边。

输出格式

仅输出一行,包含一个数,表示选点集的办法。
这个数可能很大,输出时对1000000007取模。

样例输入 1

4 1
2 1 3 2
1 2
1 3
3 4

样例输出 1

8

这道题由于有点权,让我想到DP,但是如果把状态定成差值,就很难转移,就卡住了。

由于这题的难点在于确定最大最小点的位置,所以我们定f[i]为以i为根的子树的方案数

不难想到转移方程:f[i]=π(f[j]+1) 

还有一个问题就是差值的限制,我开始想把它定入状态,但是无法转移

 

但是注意,题目说的是“连通块”,这意味着根节点是不确定的

我们只要枚举每一个点x为根,以它为最大值,这样只要满足任意v[x]-k<=v[y]<=v[x]就可以了

但是,当两个相邻的点权值一样时,它们可能会互相搜到。

这时候我们人为地定下优先度,一个点只能搜比它编号小的点,问题解决

回头来看,除了一个树的遍历,这道题没有用任何高级算法,但是蕴含着“化动为静”的思想

既然不能确定哪一个点是最大点,我们就令每一个点为最大点

既然可能存在重复计算,我们就为计算制定限制和规则

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
	ll to;ll next;
}e[4010];
ll last[2010],tot;
void add(ll x,ll y){
	e[++tot].to=y;
	e[tot].next=last[x];
	last[x]=tot;
}
ll n,k,a[2010],f[2010],mod=998244353;
ll dfs(ll x,ll fa,ll root){
	ll tot=1;
	for(ll i=last[x];i;i=e[i].next){
		ll y=e[i].to;
		if(y!=fa&&a[root]>=a[y]&&a[root]-a[y]<=k&&(root>y||a[root]!=a[y])){
			tot=(tot*(dfs(y,x,root)+1))%mod;
		}
	}
	return tot;
}
int main()
{
	ll i,j,x,y;
	scanf("%lld%lld",&n,&k);
	for(i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(i=1;i<=n-1;i++){
		scanf("%lld%lld",&x,&y);
		add(x,y);add(y,x);
	}
	ll ans=0;
	for(i=1;i<=n;i++){
		ans+=dfs(i,i,i);ans%=mod;
		//cout<<dfs(i,i,i)<<endl;
	}
	cout<<ans<<endl;
	return 0;
}
W英雄新技
时间限制 : - MS   空间限制 : 165536 KB 
评测说明 : 1000ms
问题描述

老张也准备沉迷于lol不能自拔。为了表示自己的诚意,老张设计了一个新英雄。这个新英雄的大招非常强势,在追人的时候能体现非常强的优势。假设召唤师峡谷是一个有n个节点,m条单向边的图。对于每一个节点x,可以把所有以x为终点的边的权值减少d(-10000<=d<=10000),同时把所有以x为起点的边的权值加上d。要让所有边的权值的最小值最大。当然,边的权值不能为负,因为这不符合召唤师峡谷的物理规律。

输入格式

有多组数据,对于每一组数据:

第一行为两个整数n,m

接下来m行,每一行三个整数a,b,c 表示从a到b有一条长度为c的道路

输出格式

对于每一个数据块输出文件仅有一行:

如果答案有且仅有一个解,输出最短道路的最大可能值

如果答案具有任意性,即有多解,输出”Infinite”

如果无解,输出”No Solution”

样例输入

2 1
1 2 10
2 1
1 2 -10
3 3
1 2 4
2 3 2
3 1 5
4 5
2 3 4
4 2 5
3 4 2
3 1 0
1 2 -1

样例输出

Infinite
Infinite
3
1

提示

n≤500,m≤2700,-10000<=d<=10000每条道路的长度保证不超过10000

题意大概就是每个点的所有入边和出边可以任意零和变化,求最小边的最大值

此题虽然以无向图为背景,但与一般的图论颇为不同

看到最小值最大,自然想到二分答案。二分后怎么判断,这时候差分约束粉墨登场!!

一般我们用差分约束系统都是求最大或最小值,判断是否有解要少见一些,但是也不能忽视。

说不定下次会出一个判断是否有任意解的。

这道题判断两种特殊情况把我弄炸了

其实对于二分来说很好判断,只要在二分下界仍然不满足条件,就是无解;在上界+1位置还是满足条件,就有任意解

 

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,k=1;char ch;
	ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')k=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*k;
}
struct node{
	int to;int next;int len;
}e[6010];
int last[510],tot;
void add(int x,int y,int z){
	e[++tot].to=y;
	e[tot].len=z;
	e[tot].next=last[x];
	last[x]=tot;
}
int n,m,lx[6010],ly[6010],wei[6010];
int dis[510],book[510],cnt[510],ss;
queue<int>q;
void spfa(){
	memset(dis,0x3f,sizeof(dis));memset(book,0,sizeof(book));memset(cnt,0,sizeof(cnt));
	while(q.size())q.pop();
	dis[0]=0;q.push(0);book[0]=1;
	while(q.size()){
		int t=q.front();q.pop();book[t]=0;
		cnt[t]++;
		for(int i=last[t];i;i=e[i].next){
			int s=e[i].to;int l=e[i].len;
			if(dis[t]+l<dis[s]){
				dis[s]=dis[t]+l;
				if(book[s]==0){
					q.push(s);book[s]=1;
					if(cnt[s]>n+1){
						ss=0;return;
					}
				}
			}
		}
	}
}
int judge(int mid){
	int i,j;
	memset(e,0,sizeof(e));memset(last,0,sizeof(last));tot=0;
	for(i=1;i<=m;i++){
		add(lx[i],ly[i],wei[i]-mid);
	}
	for(i=1;i<=n;i++)add(0,i,0);
	ss=1;spfa();
	//cout<<ss<<endl;
	if(ss==0)return 0;
	int maxn=-1,minn=0x3f3f3f3f;
	for(i=1;i<=n;i++){
		maxn=max(maxn,dis[i]);
		minn=min(minn,dis[i]);
	}
	//cout<<maxn<<" "<<minn<<endl;
	//if(maxn-minn<=20000)return 1;
	return 1;
}
int ef(int l,int r){
	int mid;
	while(l<r){
		mid=(l+r+1)>>1;
		if(judge(mid)==1)l=mid;
		else r=mid-1;
	}
	return l;
}
int main()
{
	//freopen("newhero.in","r",stdin);
	//freopen("oops.txt","w",stdout);
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		int i,j,k,a,b,c;
		memset(lx,0,sizeof(lx));memset(ly,0,sizeof(ly));memset(wei,0,sizeof(wei));
		for(i=1;i<=m;i++){
			scanf("%d%d%d",&a,&b,&c);lx[i]=a;ly[i]=b;wei[i]=c;
		}
		//cout<<judge(2418)<<endl;
		//for(i=1;i<=n;i++)cout<<dis[i]<<" ";
		if(judge(1)==0){
			cout<<"No Solution"<<endl;continue;
		}
		if(judge(10001)==1){
			cout<<"Infinite"<<endl;continue;
		}
		cout<<ef(1,10000)<<endl;
	}
	return 0;
} 
posted @ 2019-11-14 17:06  PeppaRan  阅读(284)  评论(0编辑  收藏  举报