CodeTON Round 4 (Div. 1 + Div. 2, Rated, Prizes!) (D,E)

Problem - D - Codeforces

思路:大模拟

  1. 我们只需要对所有操作1确认它至少x米可以上top,至多y米必定上top来确认是否使用,只要每次操作1对于当前区间[l,r](上top区间)有x<=r&&y>=l,那么可以采纳,并且更新l,r。否则不用
  2. 对于操作2,我们只需判断在最少米与最高米上top天数是否相同,是则采纳,否则-1
  3. 判断天数ans是否相同(以l为例),有(a-b)*(ans-1)+a>=l \Rightarrow n> =\frac{l-b}{a-b}。向上取整,所以我们给分子加上分母-1(即a-b-1),所以得到ans=\frac{l-b+a-b-1}{a-b}
#include <bits/stdc++.h>
using namespace std;
#define ll     long long
#define endl             "\n"
#define int ll
int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int t,q,op,a,b,n,x,y;
	cin>>t;
	while (t--)
		{
			cin>>q;
			int l=1,r=1e18;
			while (q--)
				{
					cin>>op>>a>>b;
					if (op==1)
						{
							cin>>n;
							if (n==1)
								{
									x=1,y=a;
								}
							else
								{
									x=1+a*(n-1)-b*(n-2);
									y=(a-b)*(n-1)+a;
								}
							if (y>=l&&x<=r)//判断是否采用,是则更新区间,尽量缩小它
								{
									cout<<1<<" ";
									l=max(l,x),r=min(r,y);
								}
							else cout<<0<<" ";
						}
					else
						{
							x=a>=l?1LL:(l-b+a-b-1)/(a-b);
							y=a>=r?1LL:(r-b+a-b-1)/(a-b);
							if (x==y)cout<<x<<" ";
							else cout<<-1<<" ";
						}
				}
			cout<<endl;
		}
	system("pause");
	return 0;
}

 Problem - E - Codeforces

思路:并查集

  1.  如果有解,必然存在从一个u(a[u]=0),可以走过所有点。
  2. 我们可以走所有a[i]=0的点(前提是它没有被别人来过),然后记录这个点可以走过的所有点(把这些点都归为当前i这个集合,并且存储这个集合的点数)
  3. 我们每次走时,如果遇到集合u(或者点),并且我x(从x出发)当前消灭怪物数,那么我把这个集合吸收。
  4. 遍历完后,如果有一个点可以吸收所有点,有解
#include <bits/stdc++.h>
using namespace std;
#define ll     long long
#define endl             "\n"
typedef pair<long long, long long> pll;
const int N = 2e5 + 10;

int a[N],fa[N],cnt[N];
int vis[N];
struct node
{
	int next,to;
} edge[N<<1];
int head[N],num;
int t,n,m;

inline void init()
{
	for (int i=0; i<=n; ++i)head[i]=0,fa[i]=i,cnt[i]=1,vis[i]=0;
	num=0;
}

inline void add(int u,int v)
{
	edge[++num].next=head[u];
	edge[num].to=v;
	head[u]=num;
}

inline int find(int u)//并查集找祖先
{
	if (u!=fa[u])fa[u]=find(fa[u]);
	return fa[u];
}

bool check(int x)//检查从x出发是否可以经过所有点
{
	set<pll>s;//用set存储,每次取a[i]最小的点出来
	s.insert({a[x],x});
	while (!s.empty())
		{
			int u=(*s.begin()).second;
			s.erase(s.begin());
			if (a[u]>cnt[x])return (cnt[x]==n);//如果这个点到不了,后面都到不了,直接返回
			int tmp=find(u);
			if (tmp!=x)fa[tmp]=x,cnt[x]+=cnt[tmp],cnt[tmp]=0;//如果这个点的祖先不是x,吸收这个祖先的集合
			for (int i=head[u]; i; i=edge[i].next)
				{
					int v=edge[i].to;
					if (vis[v]<x)//vis两个功能,1个是判断a[i]=0的点有没有被访问过,有则main函数不用来了,而是一个check(x)回合避免多次访问一个点v
						vis[v]=x,s.insert({a[v],v});
				}
		}
	return (cnt[x]==n);
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>t;
	while (t--)
		{
			cin>>n>>m;
			init();
			for (int i=1; i<=n; ++i)cin>>a[i];
			int x,y;
			for (int i=1; i<=m; ++i)
				{
					cin>>x>>y;
					add(x,y),add(y,x);
				}
			bool flag=0;
			for (int i=1; i<=n; ++i)if (a[i]==0&&!vis[i])if (check(i))
						{
							cout<<"YES"<<endl;
							flag=1;
							break;
						}
			if (!flag)
				cout<<"NO"<<endl;
		}
	system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值