Codeforces EDU100

A:题目大意:有三个怪物血量a,b,c 每回合可以对一个怪物造成一点伤害,每7回合对三个怪物都造成一点伤害,问是否能够在某一回合同时击杀三个怪物。

每7个回合总共造成9点伤害,由此可以求出总的回合数,只需要看最小的是否小于回合数即可

B:题目大意:你有一个序列a,希望构造一个序列b,使得b满足

1.bi在1~1e9范围内

2.bi和bi+1能够整除

3.2 * |ai - bi| <= S S为a中元素的和

观察到前面的系数2,所以考虑用2的倍数来构造

对于任意一个数都在连续的2的幂次中间,即2 ^ i <= aj <= 2 ^ (i + 1),每次选取靠近x的那边,可以证明每次选取的值ai, bi做差之后的绝对值均小于等于x / 2

如此构造即可,注意上界1e9的条件,特殊判断一下2 ^ 29 - 2 ^ 30的情况

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define db double
using namespace std;

int read() {
	int rt = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <='9') {
		rt = (rt << 3) + (rt << 1) + ch - '0';
		ch = getchar();
	}
	return rt * f;
}
const int maxn = 55;
int mi[32];
int T, n, a[maxn], b[maxn];
int main() {
	T = read();
	mi[0] = 1;
	for (int i = 1; i <= 30; i++) mi[i] = mi[i - 1] << 1;
	while (T--) {
		n = read();
		for (int i = 1; i <= n; i++) a[i] = read();
		for (int i = 1; i <= n; i++) {
			int pos;
			for (int j = 0; j <= 30; j++) {
				if (mi[j] <= a[i] && mi[j + 1] > a[i]) {
					pos = j;
					break;
				}
			}
			if (pos == 29) {
				b[i] = mi[pos];
				continue;
			}
			int tmp1 = a[i] - mi[pos];
			int tmp2 = mi[pos + 1] - a[i];
			if (tmp1 <= tmp2) b[i] = mi[pos];
			else b[i] = mi[pos + 1];
		}
		for (int i = 1; i <= n; i++) printf("%d ", b[i]);
		puts("");
	}
	return 0;
}
/*
8
3
1 5
3 0
6 4
3
1 5
2 4
10 -5

1
5
2 -5
3 1
4 1
5 1
6 1
4
3 3
5 -3
9 2
12 0
8
1 1
2 -6
7 2
8 3
12 -9
14 2
18 -1
23 9
5
1 -4
4 -7
6 -1
7 -3
8 -7
2
1 2
2 -2
6
3 10
5 5
8 0
12 -4
14 -7
19 -5

1
3
0 2
2 2
2 5 
*/

C:题目大意:

你有一个机器人,这个机器人在一维坐标轴上移动。你可以给这个机器人下达指令,指令的形式为 ti,xi ,意味着机器人在第ti秒的时候获得一条指令,此时这个机器人以1/s的速度从现在的位置开始向xixi移动。若机器人执行当前指令的过程中收到其他命令,那么其他命令会被忽略。

现在问你给出的指令中被成功执行的命令有几条,被成功执行的命令的定义并不是字面意义上的成功之行,题目给出的成功之行命令的条件为:

假设当前命令为 ti,xi , 若机器人在 ti到 ti+1这个时间区间(包括边界)内到达过 xi 这个位置,则称这条命令被成功执行。

对于最后一条命令 tn,xn,我们认为有一个tn+1=∞ 。

考虑进行模拟

每个点的答案只会在ti -ti+1这段时间更新,所以模拟每段时间发生的事情。

考虑记录一下每次前进的方向和时间

两种情况

1.到下一个t的时候,原来前进还没有结束

2.在中间或者末尾的时候结束了

对于这两种情况,机器人在这个事件段内的活动都是单调的,可以找出对应的活动范围L-R

对于1的情况 行动时间对应减少就可以了

对于2的情况,更新行动的方向和时间

模拟完即可

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define db double
using namespace std;

ll read() {
	ll rt = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <='9') {
		rt = (rt << 3) + (rt << 1) + ch - '0';
		ch = getchar();
	}
	return rt * f;
}

const int maxn = 1e5 + 5;

ll T, a, b, c, n, t[maxn], x[maxn];

int main() {
	T = read();
	while (T--) {
		n = read();
		for (int i = 1; i <= n; i++) t[i] = read(), x[i] = read();
		ll d, tim;
		ll pos = 0;
		if (x[1] > 0) d = 1, tim = x[1];
		else if (x[1] == 0) d = 0, tim = 0;
		else d = -1, tim = -x[1];
		t[n + 1] = 1e14;
		ll ans = 0;
		for (int i = 2; i <= n + 1; i++) {
			if (t[i] - t[i - 1] >= tim) {
				ll tmp = pos + d * tim;
				ll L = min(tmp, pos);
				ll R = max(tmp, pos);
				if (x[i - 1] >= L && x[i - 1] <= R) ans++;
				pos = tmp;
				if (x[i] > pos) d = 1, tim = (x[i] - pos);
				else if (x[i] == pos) d = 0, tim = 0;
				else d = -1, tim = (pos - x[i]);
			} else {
				ll tmp = pos + d * (t[i] - t[i - 1]);
				ll L = min(tmp, pos);
				ll R = max(tmp, pos);
				if (x[i - 1] >= L && x[i - 1] <= R) ans++;
				pos = tmp;
				tim -= (t[i] - t[i - 1]);
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}
/*
8
3
1 5
3 0
6 4
3
1 5
2 4
10 -5

1
5
2 -5
3 1
4 1
5 1
6 1
4
3 3
5 -3
9 2
12 0
8
1 1
2 -6
7 2
8 3
12 -9
14 2
18 -1
23 9
5
1 -4
4 -7
6 -1
7 -3
8 -7
2
1 2
2 -2
6
3 10
5 5
8 0
12 -4
14 -7
19 -5

1
3
0 2
2 2
2 5 
*/

D:题目大意:有一个2n的排列组成了n对,n对中有x对选了小的值,有n-x选了大的值,给出一个选择后的序列,问可能的x共有多少种

将排列分为两部分,一部分是最终答案,一部分是拿来配对的。

考虑x的最小最大值

x取最小的时候,即对于选择后的序列中的元素优先考虑比他小的数拿来配对

这种情况要分类,类似于田忌赛马那样的贪心

维护双指针

如果配对序列中的最小值已经不能满足了,那就去搞掉最大值

如果可以满足条件就配对

x取最大值的时候,优先考虑比他大的数拿来配对

这种情况按顺序排好之后对应配对就可以了

现在求出了最小最大值,由于交换两个数的位置,最多对答案的产生1的贡献,那么最小最大值都可以取到,那么总的可能就是最小到最大值中的所有值。

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define ll long long
#define db double
using namespace std;

int read() {
	int rt = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <='9') {
		rt = (rt << 3) + (rt << 1) + ch - '0';
		ch = getchar();
	}
	return rt * f;
}
const int maxn = 2e5 + 5;
int T, n, b[maxn], a[maxn], cnt;	
bool vis[maxn << 1];
int main() {
	T = read();
	while (T--) {
		n = read();
		cnt = 0;
		for (int i = 1; i <= 2 * n; i++) vis[i] = 0;
		for (int i = 1; i <= n; i++) b[i] = read(), vis[b[i]] = 1;
		for (int i = 1; i <= 2 * n; i++) {
			if (!vis[i]) a[++cnt] = i;
		}
		int lf = 1, ma = 0, mi = 0, rg = n;
		for (int i = 1; i <= n; i++) {
			while (lf <= rg && a[lf] < b[i]) lf++;
			if (lf > rg) break;
			ma++;
			lf++;
		}
		lf = 1;
		rg = n;
		for (int i = 1; i <= n; i++) {
			if (lf > rg) break;
			if (b[i] < a[lf]) {
				mi++;
				rg--;
			} else {
				lf++;
			}
		}
		printf("%d\n", ma - mi + 1);
	}
	return 0;
}
/*
8
3
1 5
3 0
6 4
3
1 5
2 4
10 -5

1
5
2 -5
3 1
4 1
5 1
6 1
4
3 3
5 -3
9 2
12 0
8
1 1
2 -6
7 2
8 3
12 -9
14 2
18 -1
23 9
5
1 -4
4 -7
6 -1
7 -3
8 -7
2
1 2
2 -2
6
3 10
5 5
8 0
12 -4
14 -7
19 -5

1
3
0 2
2 2
2 5 
*/

E:题目大意:有许多约束条件,有些是大致的大小关系,有些必须相邻,问是否存在一种合法排列并输出

首先考虑不合法情况,即中间成环的情况

剩下考虑如何解决相邻

既然已经相邻那么缩点就可以了

最后跑topsort来得到答案

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e6+100;

int p[N],f[N],nt[N],c[N],ord[N],ban[N],du[N],id;

vector<int>node[N],dcc[N],ans;

int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}

void merge(int x,int y)//y->x
{
	int xx=find(x),yy=find(y);
	if(xx!=yy)
		f[yy]=xx;
}

int topo(int st)
{
	int cnt=0;
	queue<int>q;
	q.push(st);
	while(q.size())
	{
		int u=q.front();
		q.pop();
		cnt++;
		for(auto it:dcc[u])
			ans.push_back(it);
		for(auto v:node[u])
			if(--du[v]==0)
				q.push(v);
	}
	return cnt;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int n,m,rt;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",p+i);
		if(p[i]==0)
			rt=i;
	}
	for(int i=1;i<=m;i++)//并查集缩点
	{
		int u,v;
		scanf("%d%d",&u,&v);
		nt[u]=v;
		ban[v]=true;//v被缩到u的集合中去,在新图中相当于被ban掉了
		merge(u,v);
	}
	for(int i=1;i<=n;i++)//缩点
	{
		if(ban[i])
			continue;
		id++;
		int pos=i;
		while(pos&&!c[pos])//注意这里会出现环的情况
		{
			ord[pos]=dcc[id].size();
			dcc[id].push_back(pos);
			c[pos]=id;
			pos=nt[pos];
		}
	}
	for(int i=1;i<=n;i++)//如果拓扑存在环的话,就不会被遍历到
		if(!c[i])
			return 0*puts("0");
	for(int i=1;i<=n;i++)//建边
	{
		if(i==rt)
			continue;
		if(c[i]!=c[p[i]])
		{
			node[c[p[i]]].push_back(c[i]);
			du[c[i]]++;
		}
		else if(ord[p[i]]>ord[i])//特判
			return 0*puts("0");
	}
	if(topo(c[rt])!=id)//拓扑中有环
		return 0*puts("0");
	for(auto it:ans)
		printf("%d ",it);
	puts("");
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值