2020 牛客多校第三场

https://ac.nowcoder.com/acm/contest/5667

RK:166

Solved:6 / 12

UpSolved:8 / 12


A、Clam and Fish

有四种类型的池塘,类型0的池塘没有鱼也没有鱼饵,类型1的池塘没有鱼有鱼饵,类型2的池塘有鱼没有鱼饵,类型三的池塘有鱼也有鱼饵,每一天只能进行一次操作,如果你有鱼饵即使池塘没有鱼你也可以抓到鱼,如果池塘有鱼你可以不用鱼饵抓鱼,按顺序在每个池塘进行一次操作,问最多能钓多少条鱼

按题意模拟即可,将所有类型1的池塘用来作鱼饵,然后将最后剩下的鱼饵数/2 表示将后边一半的天数使用前一半的天数做鱼饵来钓鱼。

#include
using namespace std;
const int N = 2e6 + 5;
char s[N];
int main() {
	int _;
	scanf("%d", &_);
	while (_--) {
		int n;
		scanf("%d", &n);
		scanf("%s", s + 1);
		int ls = n;
		int ans = 0, t = 0;
		for (int i = 1; i <= ls; i++) {
			if (s[i] == '3')
				ans++;
			else if (s[i] == '2')
				ans++;
			else if (s[i] == '0') {
				if (t >= 1)
					ans++, t--;
			}
			else if (s[i] == '1')
				t++;
		}
		printf("%d\n", ans + t / 2);
	}
}

B、Classical String Problem

多次操作,每次操作将前面x个字符移到最后面或者将后面x个字符移到最前面,并且要支持查询当前的下标为x的字符

无论怎么移动,都一定是一个环,所以对于每次的移动操作,我们只需要维护一个当前起点即可。

#include
using namespace std;
typedef long long ll;
const int MAX = 2e6 + 5;
const int INF = 0x3f3f3f3f;
char s[MAX], c[MAX];
int q, x, op, len;
void solve() {
    scanf("%s", s);
    len = strlen(s);
    scanf("%d", &q);
    while (q--) {
        scanf("%s%d", c, &x);
        if (c[0] == 'A') {
            int left = op, right = len - op;
            if (x <= right) {
                printf("%c\n", s[left + x - 1]);
            }
            else {
                printf("%c\n", s[x - right - 1]);
            }
        }
        else if (c[0] == 'M') {
            op += x;
            while (op < 0) op += len;
            op %= len;
        }
    }
}
int main(void)
{
    solve();
    return 0;
}

C、Operation Love

给一个右手手掌的图案,可以旋转、平移,左手的图案是右手图案的对称图案,给一个图案,问你是左手图案还是右手图案。

先判断一个顺时针还是逆时针,如果是逆时针将它反转,然后判断6和9,根据谁在前面,判断是那只手的图案。

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=44276347

int main()
{
	int T;
	sc("%d", &T);
	while (T--)
	{
		polygon p;
		p.input(20);
		if (p.getdir() == 1)
		{
			for (int i = 1; i <= 9; i++)
				swap(p.p[i], p.p[20 - i]);
		}
		//顺指针
		vectorv;
		for (int i = 0; i < 20; i++)
		{
			//cout << p.p[(i + 1) % 20].distance(p.p[i]) << endl;
			//cout << (int)(p.p[(i + 1) % 20].distance(p.p[i]) + eps) << endl;
			v.push_back(p.p[(i + 1) % 20].distance(p.p[i]));
		}
		for (int i = 0; i < 20; i++)
		{
			if (sgn(v[i] - 6) == 0 && sgn(v[(i + 1) % 20] - 9) == 0)
			{
				pr("left\n");
				goto qwe;

			}
		}
		pr("right\n");
	qwe:;
	}
}

D、Points Construction Problem

起初所有点都是黑色的,给你 n 次操作,每次操作可以将一个点变成白色,要求操作后,有 m 对点满足这一点对相邻并且颜色不同。

首先每个点可以贡献4对点对,所以我们需要考虑 x 个点构成的图案最多可以优化掉多少对点,然后可以发现,当构成正方形的时候可以优化的点对尽量多,所以我们考虑在正方形的基础上,上边加上一层右边加上一层,使其变成(n+1)*(n+1)的正方形,并以此为基础求出每个点最多可以优化的边的数量

其中 a 矩阵表示按照上述构造方法第 n 个点最多可以优化的边的数量

#include 
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
int a[] = { 0,0,
2,2,4,
2,4,2,4,4,
2,4,4,2,4,4,4,
2,4,4,4,2,4,4,4,4,
2,4,4,4,4,2,4,4,4,4,4,
2,4,4,4,4,4,2,4,4,4,4,4,4,
2 };
int n, m;
int tx, ty;
void print(int n)
{
	for (int q = 7; q >= 1; q--)
	{
		if (q * q <= n)
		{
			for (int i = tx; i < tx + q; i++)
			{
				for (int j = ty; j < ty + q; j++)
				{
					pr("%d %d\n", i, j);
				}
			}
			n -= q * q;

			if (n <= q + 1)
			{
				for (int j = ty; j < ty + n; j++)
				{
					pr("%d %d\n", tx + q, j);
				}
			}
			else
			{
				for (int j = ty; j < ty + q; j++)
				{
					pr("%d %d\n", tx + q, j);
				}
				n -= q;
				for (int i = tx; i < tx + n; i++)
				{
					pr("%d %d\n", i, ty + q);
				}
			}
			tx += q + 10; ty += q + 10;
			break;
		}
	}
}
int main()
{
	for (int i = 1; i <= 50; i++)
		a[i] += a[i - 1];
	int T;
	sc("%d", &T);
	while (T--)
	{
		tx = 1, ty = 1;
		sc("%d%d", &n, &m);
		if (m & 1)
		{
			pr("No\n");
			continue;
		}
		if (m < 4 * n - a[n] || m > 4 * n)
		{
			pr("No\n");
			continue;
		}
		pr("Yes\n");
		int remain = 4 * n - m;
		while (remain)
		{
			if (remain == 2)
			{
				pr("%d %d\n", 0, 1);
				n--;
				break;
			}
			for (int i = n; i >= 1; i--)
			{
				if (a[i] <= remain)
				{
					remain -= a[i];
					print(i);
					n -= i;
					break;
				}
			}
		}
		while (n)
		{
			pr("%d %d\n", tx, ty);
			tx += 10, ty += 10;
			n--;
		}
	}
}

E、Two Matchings

有 n 个点,每个点一个值,要求求出两个 1 - n 的排列满足排列的任意两个相同下标的值都不同,并且这两个排列要满足下标不等于值,并且要满足两两交换,即以这个下标的值为下标的值等于这个下标,换句话来说,就是两两一组交换下标。求出按照这两个排列分成的n/2组数对的差的绝对值之和的最小值并输出

首先最小的置换一定是将数组排序后,相邻的两个数字的下标交换,考虑次小,一定是4个数字一组交换或者6个数字一组交换最优,8个一组可以拆成 4 和 4,2个一组和之前的排列重合了,所以考虑 dp ,并且4个一组的贡献是确定的(可以通过枚举来得到次小值),假设 a

#include
using namespace std;
#define ll long long
const int N = 2e5 + 5;
ll c[N], dp[N][2][2];
int main() {
    int _;
    scanf("%d", &_);
    while (_--) {
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &c[i]);
            dp[i][0][0] = dp[i][0][1] = dp[i][1][0] = dp[i][1][1] = 1e18;
        }
        sort(c + 1, c + n + 1);
        ll anst = 0;
        for (int i = 1; i <= n; i += 2)
            anst += c[i + 1] - c[i];
        for (int i = 1; i <= n; i++) {
            if (i >= 4) {
                ll t = c[i] - c[i - 3] + c[i - 1] - c[i - 2];
                for (int j = 0; j <= 1; j++) {
                    for (int k = 0; k <= 1; k++) {
                        ll tt = t + dp[i - 4][j][k];
                        if (tt < dp[i][0][0])
                            dp[i][0][1] = dp[i][0][0], dp[i][0][0] = tt;
                        else if (tt < dp[i][0][1])
                            dp[i][0][1] = tt;
                    }
                }
            }
            if (i >= 6) {
                ll t = c[i] + c[i - 1] + c[i - 3] - c[i - 2] - c[i - 4] - c[i - 5];
                for (int j = 0; j <= 1; j++) {
                    for (int k = 0; k <= 1; k++) {
                        ll tt = t + dp[i - 6][j][k];
                        if (tt < dp[i][1][0])
                            dp[i][1][1] = dp[i][1][0], dp[i][1][0] = tt;
                        else if (tt < dp[i][1][1])
                            dp[i][1][1] = tt;
                    }
                }
            }
        }
        printf("%lld\n", anst+min(dp[n][1][0], dp[n][0][0]));
    }
}

F、Fraction Construction Problem

T组询问,每次询问给出 a,b,求满足 \frac{c}{d}-\frac{e}{f}=\frac{a}{b} 的 c,d,e,f 的值,若不存在输出-1 -1 -1 -1

要求 d

先将左边通分,并且将 b 分解为两个互质的数 d、f,并且满足 d*f=b,且 d f 均不等于 1,所以题目就变成了求 c*f-e*d=a,这就是 exgcd 能解决的问题,由于要满足 c 大于0,所以若 c 小于0,c/d 增加1,e/f减少1 即可,然后记得题目中的是负号,所以给 e 加一个负号即可。(不过这里有些问题,为啥 e 后面一定是负数呢?感觉自己对于exgcd理解不够,有时间可以再学一波)

#include 
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2000005;
bool vis[MAXN];
int v[MAXN];
ll gcd(ll a, ll b)
{
	return b == 0 ? a : gcd(b, a % b);
}
ll exgcd(ll a, ll b, ll& x, ll& y) ///ax+by = gcd(a,b);
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	ll gcd = exgcd(b, a % b, x, y);
	ll tmp = x;
	x = y;
	y = tmp - a / b * (y);
	return gcd;
}
void init()
{
	for (int i = 2; i < MAXN; i++)
	{
		if (vis[i] == false)
		{
			for (int j = i + i; j < MAXN; j = j + i)
			{
				vis[j] = true;
				v[j] = v[j] == 0 ? i : min(v[j], i);
			}
		}
	}
}
int main()
{
	init();
	int T;
	sc("%d", &T);
	while (T--)
	{
		ll a, b;
		sc("%lld%lld", &a, &b);
		ll g = gcd(a, b);
		if (g != 1)
		{
			pr("%lld %lld %lld %lld\n", a / g + 1, b / g, 1LL, b / g);
		}
		else
		{
			if (v[b] == 0)
			{
				pr("-1 -1 -1 -1\n");
				continue;
			}
			ll d = v[b], f = b;
			while (f % d == 0)
				f /= d;
			d = b / f;
			if (d == 1 || f == 1)
			{
				pr("-1 -1 -1 -1\n");
				continue;
			}
			//c * f - e * d = a
			//x       y
			ll c, e;
			int g = exgcd(f, d, c, e);
			if (a % g != 0)
			{
				pr("-1 -1 -1 -1\n");
			}
			else
			{
				if (c <= 0)
				{
					c += d;
					e -= f;
				}
				pr("%lld %lld %lld %lld\n", c * a, d, -e * a, f);
			}
		}
	}
}

G、Operating on a Graph

L、Problem L is the Only Lovely Problem

签到

#include 
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
int main()
{
	string s;
	cin >> s;
	if (s.size() < 6)
		pr("ugly");
	else
	{
		string ss = s.substr(0, 6);
		for (int i = 0; i < 6; i++)
			ss[i] = tolower(ss[i]);
		if (ss == "lovely")
			pr("lovely");
		else
			pr("ugly");
	}
}

 

你可能感兴趣的:(2020,牛客多校赛)