【NOIP2018模拟赛2018.10.26】

第一题数论,证不出来,,,

spfa统计到每个点最短路数量,乘起来就是答案。

统计方法:更新最短路就把此点最短路数量重新赋为1,遇到相等dis[x] + w == dis[to]的情况则数量++。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 1005;
const int MAXM = 1e6 + 5;
const ll MOD = 2147483647;
int n,m;
ll dis[MAXN],ans = 1;
bool vis[MAXN];
struct edge
{
	int next,to,w;
}e[MAXM<<1];
int head[MAXM<<1],cnt = 0;
void add(int u,int v,int w)
{
	e[++cnt].next = head[u]; e[cnt].to = v; e[cnt].w = w; head[u] = cnt;
	e[++cnt].next = head[v]; e[cnt].to = u; e[cnt].w = w; head[v] = cnt;
}
int ing[MAXN];
void in(int &x)
{
	int num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

int q[MAXN<<2];

void spfa()
{
	memset(dis,0x3f,sizeof dis);	
	memset(vis,0,sizeof vis);
	int h = 0,t = 0;
	q[++t] = 1; vis[1] = 1; dis[1] = 0;
	while(h < t)
	{
		int x = q[++h]; vis[x] = 0;
		for(int i = head[x];i;i = e[i].next)
		{
			int to = e[i].to,w = e[i].w;
			if(dis[to] > dis[x] + w)
			{
				dis[to] = dis[x] + w;
				ing[to] = 1;
				if(!vis[to]) vis[to] = 1,q[++t] = to;
			}
			else if(dis[to] == dis[x] + w)ing[to]++;
		}
	}
}

int main()
{
//	freopen("tree.in","r",stdin);
//	freopen("tree.out","w",stdout);
	in(n); in(m);
	for(int i = 1;i <= m;i++)
	{
		int x,y,z; in(x),in(y),in(z);
		add(x,y,z);
	}
	spfa();
	for(int i = 1;i <= n;i++)
		if(ing[i] > 1) ans = ans * ing[i] % MOD;
	out(ans);
	return 0;
}

贪心 + multiset 二分  之所以用multiset是因为不同草的价格可能一样,都得统计入答案选择中。

先按fresh程度把牛和草从大到小排序,然后枚举每头牛(按fresh程度从大到小),每次把fresh大于等于牛要求的草加入到multiset里,然后在multiset直接二分查找(lower_bound)大于等于牛要求的价格的草,、。

若找不到肯定无解,输出-1;找到就将草价格加入答案,把找到草的信息从multiset中删除。

贪心可以证明正确性。我才不是懒得写怎么证确实是懒,不证了。

代码:(跑的挺快的?)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ubr int
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 1e6 + 5;
int n,m;
int maxfresh = 0,maxcost = 0;
int fresh = 0,cost = 0;
ll ans = 0;
struct cow
{
	int cost,fresh;
	bool operator < (const cow one) const
	{
		return fresh > one.fresh;
	}
}c[MAXN<<1],cao[MAXN<<1];
//struct grass
//{
//	int cost,fresh;
//	bool operator < (const grass one) const
//	{
//		return fresh > one.fresh;
//	}
//}cao[MAXN<<1];
void in(int &x)
{
	int num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}
multiset<int> s;
multiset<int> :: iterator it;
int main()
{
	in(n); in(m);
	if(m < n) {cout << -1; return 0;}
	
	for(int i = 1;i <= n;i++) 
	{
		in(c[i].cost),in(c[i].fresh);
		maxcost = max(maxcost,c[i].cost);
		maxfresh = max(maxfresh,c[i].fresh);
	}
	for(int i = 1;i <= m;i++) 
	{
		in(cao[i].cost),in(cao[i].fresh);
		cost = max(cost,cao[i].cost);
		fresh = max(fresh,cao[i].fresh);
	}
	
	if(maxcost > cost || maxfresh > fresh) {cout << -1; return 0;}
	
	sort(c+1,c+1+n); sort(cao+1,cao+1+m);
	int j = 1;
	for(int i = 1;i <= n;i++)
	{
		while(cao[j].fresh >= c[i].fresh && j <= m) s.insert(cao[j++].cost);
		it = s.lower_bound(c[i].cost);
		if(it == s.end()) {out(-1); return 0;}
		ans += *it;
		s.erase(it);
	}
	out(ans);
	return 0;
}

嘛这道题刚刚看到会迷茫是当然的,因为其算法是乱搞出来的。(也许是???),题目又扯联通块又扯树操作,数据范围明显随性,自然想到应该乱搞。

具体来说,这道题基本思路就是枚举每个点作为其可以在的联通块里的最大值点进行dfs,遇见比它值小的点且两者之差不大于k就继续dfs下去,直到不满足上面的条件。

方案数统计方法看代码,乘法原理不想多说。

具体需要注意的细节在于取模和判重。

1、取模注意步步取模(不解释了吧dalao们因为这个一路丢了多少分不再累述)

2、判重在于如果有两个点点权一样大,那么它们互相会搜到对方的情况(当然前提是能搜到对方)是会重复统计的,我们想避免的话该怎么办呢?

人为规定,当点权相等时,只能从编号小节点搜到编号大的节点,这样就不会重复统计了。(具体看代码dfs中最后俩个判断)

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 1e4 + 5;
const int MAXM = 1e4 + 5;
const ll MOD = 19260817;
int n; ll k;
ll dis[MAXN],ans1 = 0,ans2 = 0;
ll w[MAXN];
bool vis[MAXN];
struct edge
{
	int next,to;
}e[MAXM<<1];
int head[MAXM<<1],cnt = 0;
void add(int u,int v)
{
	e[++cnt].next = head[u]; e[cnt].to = v; head[u] = cnt;
	e[++cnt].next = head[v]; e[cnt].to = u; head[v] = cnt;
}
int ing[MAXN];
void in(int &x)
{
	int num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

ll dfs(int x,int fr,int Max)
{
	ll now = 1;
	for(int i = head[x];i;i = e[i].next)
	{
		int to = e[i].to;
		if(to != fr && w[Max] >= w[to] && w[Max] - w[to] <= k
			&& (w[Max] != w[to] || Max < to))
			now = (now * (dfs(to,x,Max) + 1)) % MOD;
	}
	return now % MOD;
}

int main()
{
	in(n); lin(k);
	for(int i = 1;i <= n;i++) lin(w[i]);
	for(int i = 1;i < n;i++)
	{
		int x,y; in(x),in(y);
		add(x,y);
	}
	for(int i = 1;i <= n;i++)
		ans1 = (ans1 + dfs(i,0,i) % MOD) % MOD;
	if(k){
		k--;
		for(int i = 1;i <= n;i++)
			ans2 = (ans2 + dfs(i,0,i) % MOD) % MOD;
	}
	out((ans1 - ans2 + MOD) % MOD);
	return 0;
}

ps:被同学说代码风格新奇,但是我觉得很普通啊,若哪位大佬也觉得丑的一批请告诉我,避免蒟蒻越走越偏。。

猜你喜欢

转载自blog.csdn.net/chang_yl/article/details/83421671