2024-08-21-ABC367

A - Shout Everyday

高桥从B睡到C,如果在A时刻时,他醒着就会尖叫,问他是不是会尖叫。

说白就是问你A在不在BC两个时刻之间。

#include<bits/stdc++.h>
using namespace std;
int A, B, C;
int main() {
	cin >> A >> B >> C;
	if ((A - B + 24) % 24 > (C - B + 24) % 24) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }
	return 0;
}

也可以从B循环到C。

B - Cut .0

给定一个小数,去除后导0,如果删完小数部分则还要把小数点删了。

#include <bits/stdc++.h>
using namespace std;
 
int main() {
    string s;
    cin >> s;
    while (s.back() == '0')
        s.pop_back();
    if (s.back() == '.')
        s.pop_back();
    cout << s << '\n';
 
    return 0;
}
 

C - Enumerate Sequences

要求输出所有符合要求的数组a,要求数组的和是k的倍数, 并且每个数的范围是1~ri。

枚举

#include <bits/stdc++.h>
using namespace std;
const int N = 201000;
int n, k, r[N], b[N];
void dfs(int x, int s) {
	if (x == n + 1) {
		if (s == 0) {
			for (int i = 1; i <= n; i++) printf("%d ", b[i]);
			puts("");
		}
	} else {
		for (int i = 1; i <= r[x]; i++) {
			b[x] = i;
			dfs(x + 1, (s + i) % k);
		}
	}
}

int main() {
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; i++) scanf("%d", &r[i]);
	dfs(1, 0);
	return 0;
}

D - Pedometer

环形湖上有n个点,给定ai表示从第i个点顺时针走到第i+1个点的时间。

问有多少对(s,t),满足顺时针从s走到t的时间是m的倍数。

思路

先考虑s<=t的情况,那么就是问有几段a数组的和是m的倍数,我们可以对前缀和取模,那么前缀和的模相等的点之间的和肯定是模m为0的,也就是问统计前缀和模相等的对数,具体做法:开一个cnt数组用于记数前面前缀和的模,然后枚举终点,把cnt里有多少个起点加进ans里即可。

那环形怎么办,可以小技巧:把环解成链,比如1234的环做成12341234,然后做一个类似于滑动窗口的操作,我们在走到第五位的时候把第一位在cnt里删掉,那么我们统计的就是第二位到第五位的合法s和t。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
int main() {
	int n, m;
	cin >> n >> m;
	vector<int> a(n);
	for (auto& x : a)
		cin >> x;
	vector<int> cnt(m, 0);
	LL ans = 0;
	LL presum = 0;
	for (int i = 0; i < n; ++i) {
		ans += cnt[presum % m];
		cnt[presum % m]++;
		presum += a[i];
	}
	LL sum = presum;
	presum = 0;
	for (int i = 0; i < n; ++i) {
		cnt[presum % m]--;
		ans += cnt[sum % m];
		sum += a[i];
		presum += a[i];
	}
	cout << ans << '\n';

	return 0;
}

E - Permute K times

给定数组x和a,一共进行k次操作,每次操作做一个新数组bi=axi,然后把a数组替换为b数组,问k此操作之后的数组。

思路

需要知道的常识:如果x是一个1~n的排列的话,对于像这样的变化,如果把x的每次操作画成图的话,x一定是由很多环构成的,每个环的大小必定不大于n。

不是排列的话,那么他其实是一个基环树森林,但我们不用管他,因为那些经过一次操作到达相同点的点以后所有的操作都是一样的,我们不用处理。

这题其实是一道非常模板的倍增,k非常大,我们处理一个二维数组run[64][n],其中run[i][j]表示第j个位置走\(2^i\)步之后会到哪里,这个数组非常好处理,因为第j个点走\(2^i\)步等于他从先走\(2^{i-1}\)步到达的地方再走\(2^{i-1}\)步,写成转移就是run[i][j] = run[i - 1][run[i - 1][j]],这就是倍增的基本思想。

同样,根据我们这个先走几步,再走几步的思想,我们直接把k分解成二进制位,就能直接调用run数组每次走2的整次幂倍的步数,找到每个点k步后的目标了。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
int run[64][200005], a[200005];
int main() {
	int n;
	LL k;
	cin >> n >> k;
	for (int i = 0; i < n; i++) {
		int v;
		cin >> v;
		--v;
		run[0][i] = v;
	}
	for (int i = 0; i < n; i++)
		cin >> a[i];
	for (int i = 1; i < 64; i++) {
		for (int j = 0; j < n; j++) {
			run[i][j] = run[i - 1][run[i - 1][j]];
		}
	}
	for (int i = 0; i < n; i++) {
		LL cnt = k;
		int cur = i;
		for (int j = 0; j < 64; j++) {
			if (cnt & (1LL << j)) {
				cur = run[j][cur];
			}
		}
		cout << a[cur] << " ";
	}
	return 0;
}

F - Rearrange Query

给定两个长度为n的数组a和b,并且有Q次查询,第i次查询给定li,ri,Li,Ri,问a和b里的对应区间能不能通过重新排列的方式使两者匹配。

如果要两段一样,那么两段的和必须一样,如果只用前缀和判断两段和是否一样会被构造的数据卡掉,所以我们对每一个数都给他随机映射到另一个数上,这样求和的方式就不容易被卡掉了。

这就是哈希,冲突率在这题里是不高的。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
mt19937_64 mrand(random_device{}()); 
const int N=201000;
int n,q;
ll hs[N],sa[N],sb[N];
int main() {
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) hs[i]=mrand();
	for(int i=1;i<=n;i++) {
		int a;
		scanf("%d",&a);
		sa[i]=sa[i-1]+hs[a];
	}
	for(int i=1;i<=n;i++) {
		int a;
		scanf("%d",&a);
		sb[i]=sb[i-1]+hs[a];
	}
	while(q--){
		int l1,r1,l2,r2;
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		puts((sa[r1]-sa[l1-1]==sb[r2]-sb[l2-1])?"Yes":"No");
	}
	return 0;
}
posted @ 2024-11-30 23:27  ~清渠~  阅读(1)  评论(0编辑  收藏  举报