Codeforces Round #699 (Div. 2) 2/6

比赛链接

T1

中文题意:
你现在在(0,0)点,有四种操作对应上下左右四个方向,现在给出终点坐标(x,y),以及指令集S是一个只包含四个字母的字符串,对应的动作你可以选择执行也可以选择不执行,询问你最终能不能到达(x,y)点。T组输入。
T ≤ 1000 , ∣ s ∣ ≤ 1 0 5 T\leq1000, |s|\leq10^5 T1000,s105

因为原点出发,指令可以执行也可以不执行,那么就认准2个方向就行了,判断这两个方向是不是满足长度大于等于(x,y)

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
#define rep(i, sta, en) for(int i=sta; i<=en; ++i)
#define repp(i, sta, en) for(int i=sta; i>=en; --i)
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar())	s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)	putchar(op); return; }	char F[40]; ll tmp = x > 0 ? x : -x;	if (x < 0)putchar('-');	int cnt = 0;	while (tmp > 0) { F[cnt++] = tmp % 10 + '0';		tmp /= 10; }	while (cnt > 0)putchar(F[--cnt]);	if (op)	putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1;	while (b) { if (b & 1)	ans *= a;		b >>= 1;		a *= a; }	return ans; }	ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 7;

ll n, m;
char s[N];

void solve() {
	n = read(), m = read();
	int cnt1 = 0, cnt2 = 0;
	scanf("%s", s + 1);
	int len = strlen(s + 1);
	rep(i, 1, len) {
		if (n >= 1 and s[i] == 'R')	++cnt1;
		if (n <= -1 and s[i] == 'L')	++cnt1;
		if (m >= 1 and s[i] == 'U')	++cnt2;
		if (m <= -1 and s[i] == 'D')	++cnt2;
	}
	if (cnt1 >= abs(n) and cnt2 >= abs(m))
		puts("YES");
	else puts("NO");
}

int main() {
	int T = read();	while (T--)
		solve();
	return 0;
}

T2

T组输入,每组输入包含一个n,m,代表你最多要填充的m个石头,以及n堆石头起始高度。
每次你都会从 a 1 a_1 a1处丢下一颗石头,你必须保证 a i ≥ a i + 1 a_i\geq a_{i+1} aiai+1才可以正常往后面通行。否则它就会停在 a i a_i ai处。如果一直运动到了 n + 1 n+1 n+1处那么这些石头都会去往虚空。问你第m块石头他会落在什么位置,如果落在虚空输出-1。
T ≤ 100 , 1 ≤ n , a i ≤ 100 , 1 ≤ m ≤ 1 0 9 T\leq100,1\leq n,a_i\leq100,1\leq m\leq10^9 T100,1n,ai100,1m109

数据范围比较小,直接模拟石子落下这个过程即可,但是注意这题给出的m很大,可能把全部的数都变成了有序的m依旧没有减成0,这个时候直接break就行了。

const int N = 100 + 7;
 
ll n, m;
ll a[N];
void solve() {
	n = read(), m = read();
	rep(i, 1, n)	a[i] = read();
	while (m) {
		int mm = m;
		rep(i, 1, n - 1) {
			if (a[i] < a[i + 1]) {
				if (m > 1)
					--m, ++a[i];
				else {
					print(i);
					return;
				}
				break;
			}
		}
		if (m == mm)	break;
	}
	print(-1);
}

T3

T组输入,第一行输入n个栅栏,m个画家。第二行n个整数 a i a_i ai代表栅栏的起始颜色。第三行n个整数 b i b_i bi代表栅栏的最终需求颜色。第四行m个整数 c i c_i ci代表m个画家,每个画家都会涂上某种颜色,但是他涂栅栏的位置可以我们安排,但是必须按照第一名画家先画完第二名画家画,这样依次下去,并且画家一定要涂色,不能空轮。问你最终有没有合理的安排方案使得栅栏变成它想要的颜色排列。如果没有输出no,如果有输出yes,以及你安排的画家涂色方案,第几个画家他涂第几个栅栏依次输出。
T ≤ 1 0 4 , n , m ≤ 1 0 5 , 1 ≤ a i , b i , c i ≤ n T\leq10^4,n,m\leq 10^5,1\leq a_i,b_i,c_i\leq n T104,n,m105,1ai,bi,cin

我们看既然要最终和答案对上,那么是不是如果有一种颜色如果没人要但是后面可以覆盖的话也是有解的。所以从这里我选择从后往前遍历画家。但是在这之前我们需要预处理一下这些栅栏颜色。
1、我们把起始颜色和目标颜色不同的栅栏编号记在一个vector数组里面,保证可以快速查询还有没有栅栏需要这个颜色。
2、我们把每个对上号的颜色打个标记,并且标记一下这个颜色可以涂在这个位置。
这样做好预处理之后呢我们就可以进行从后往前遍历的操作了。

1、如果当前处理的画家它的画笔颜色还有栅栏需要那么直接涂在需要这个颜色的栅栏位置。并且记录这个地方已经被涂过了,把它从之前对不上号的vector里面去掉这个点。
2、如果这个颜色已经涂满了,也就是没栅栏需要从别的颜色改成当前画笔的颜色,那么就查看有没有这个颜色对上号的栅栏位置,如果有那我们就涂在这个位置。
3、如果从后往前遍历到的第一个画家,他就没办法动笔的话说明无解。否则如果是第二个画家无法动笔他还可能可以涂在最后一个画家涂的位置,因为正序遍历过来的话最后一个画家会把他错误的答案修改正确。
4、进行完涂色操作后,遍历一下是不是全部的不符合的栅栏位置都变得符合了,如果还存在不符合的点依旧要输出no。其他情况都是输出yes。

const int N = 1e5 + 7;
 
ll n, m;
int a[N], b[N], c[N], ans[N];
vector<int> p[N];
int vis[N];
 
void solve() {
	ms(vis, 0);
	rep(i, 1, N - 1)	p[i].clear();
	n = read(), m = read();
	rep(i, 1, n)	a[i] = read();
	rep(i, 1, n) {
		b[i] = read();
		if (a[i] != b[i])
			p[b[i]].push_back(i);
		else vis[b[i]] = i;
	}
	rep(i, 1, m)
		c[i] = read();
	int nxt = -1;
	repp(i, m, 1) {
		if (p[c[i]].size()) {
			ans[i] = p[c[i]].back();
			p[c[i]].pop_back();
			vis[c[i]] = ans[i];
			nxt = ans[i];
		}
		else if (vis[c[i]])	ans[i] = vis[c[i]], nxt = ans[i];
		else if (nxt != -1) {
			ans[i] = nxt;
		}
		else {
			puts("NO");
			return;
		}
	}
	bool flag = 1;
	rep(i, 1, n) {
		if (p[i].size())	flag = 0;
	}
	if (!flag)	puts("NO");
	else {
		puts("YES");
		rep(i, 1, m)	print(ans[i], 32);
		puts("");
	}
}

T4

中文题意:
T组输出,每次给出一个 n ∗ n n*n nn的AB邻接矩阵,代表给你的有向图。询问能不能在有向图中找到一个长度为m的回文串。如果不能输出NO,如果可以输出YES并且给出连点顺序。
T ≤ 500 , n ≤ 1000 , m ≤ 1 0 5 T\leq500,n\leq1000,m\leq10^5 T500,n1000,m105

首先我们观察如果要我们构造回文串,当m是奇数的时候很容易构造。直接1和2两个点构造出一个奇数串即可。不管他们边对应的字母是否相同一定会出现12121这样的稳定回文串。

如果m是偶数我们再看,如果存在两个点他们之间通向的边权相同的话,也就是 m p [ i ] [ j ] = m p [ j ] [ i ] mp[i][j]=mp[j][i] mp[i][j]=mp[j][i]这样的对称点。那么显然我们可以一直按照 i j i j i j ijijij ijijij方式构造出这样的回文串,因为他们的字母都相同。

但是如果没有这样的对称点,说明我们永远无法使用2个点构造出合理解,因为任意长度为偶数的不同字母的相邻串都不可能是回文串例如ABAB等等,那既然2个点不行,我们多加一个点呢?如果要3个点构造出回文串需不需要有什么要求呢,我们把长度为偶数的全部ab回文串可以表示成下面这样的两个字符串累加。aabbaa和abba,就是两种全部可能情况了,那么我们观察无论aabbaa它们需要存在相邻的aa和bb,abba的话虽然现在看a不需要相邻的a但是如果一连接起来就变成了abbaabba了,也是需要相邻的aa和bb。而且根据我们上面的排查,已经可以确定两个点之间的来回路线会不相同也就是一个点去后一个点如果是a那么回来一定是b。所以我们只需要找到3个点他们一路向上的字母相同就找到了合理解,反之就永远不可能存在合理解因为aabbaa和abba就可以把全部只用ab字母构造的回文串表示出来。
在这里插入图片描述

const int N = 1000 + 7;

ll n, m;
char mp[N][N];
int vis[N][2];

void solve() {
	ms(vis, 0);
	n = read(), m = read();
	rep(i, 1, n) {
		scanf("%s", mp[i] + 1);
		rep(j, 1, n) {
			if (i == j)	continue;
			vis[i][mp[i][j] - 'a'] = j;
		}
	}
	if (m & 1) {
		puts("YES");
		rep(cnt, 0, m)
			if (cnt & 1)	print(2, 32);
			else print(1, 32);
		puts("");
		return;
	}
	rep(i, 1, n) {
		rep(j, 1, n) {
			if (i == j)	continue;
			if (mp[i][j] == mp[j][i]) {
				puts("YES");
				rep(cnt, 0, m) {
					if (cnt & 1)	print(j, 32);
					else print(i, 32);
				}
				puts("");
				return;
			}
		}
	}
	rep(i, 1, n) {
		rep(j, 1, n) {
			if (i == j)	continue;
			int k = vis[j][mp[i][j] - 'a'];
			if (!k)	continue;
			if ((m >> 1) & 1) { // aabbaa
				puts("YES");
				rep(cnt, 0, m) {
					if (cnt % 4 == 0)	print(i, 32);
					else if (cnt % 4 == 1)	print(j, 32);
					else if (cnt % 4 == 2)	print(k, 32);
					else print(j, 32);
				}
				puts("");
			}
			else { // abbaabba
				puts("YES");
				rep(cnt, 0, m) {
					if (cnt % 4 == 0)	print(j, 32);
					else if (cnt % 4 == 1)	print(k, 32);
					else if (cnt % 4 == 2)	print(j, 32);
					else	print(i, 32);
				}
				puts("");
			}
			return;
		}
	}
	puts("NO");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值