2021百度之星初赛二(1001 – 1003)
1001
题意:
给 a,b,每次 a,b会变为 a+b,a-b,问 k 次之后变成了哪两个数,对 998244353998244353 取模,多组数据。
题解:
首先观察前几次变化,
0 -------- a b 1 -------- a - b a + b
2 -------- 2a 2b 3 -------- 2a - 2b 2a + 2b
……
不难发现
对于k为偶数,答案是 a * pow(2,k/2) b * pow(2,k/2);
对于k为奇数,答案是 (a-b) * pow(2,(k-1)/2) (a+b) * pow(2,(k-1)/2);
ps:由于k可以达到1e9,因此使用快速幂。
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <stack>
#include <cstring>
#include <map>
#define INF 0x3f3f3f3f
#define ll long long
const double pi = acos(-1.0);
using namespace std;
const ll mod = 998244353;
ll qpow(ll a, ll b) {
ll ans = 1;
while (b) {
if (b & 1) {
ans = (ans * a) % mod;
}
b >>= 1;
a = (a * a) % mod;
}
return ans;
}
int main() {
int t; cin >> t;
while (t--) {
ll a, b, c, d;
cin >> a >> b;
c = (a + b) % mod;
d = (a - b + mod) % mod;//这里的mod一定要加,像我不加卡半小时
ll k; cin >> k;
ll s = k / 2;
ll e = k % 2;
ll pre = qpow(2, s);
ll ans1, ans2;
if (e & 1) {
ans1 = (c * pre) % mod;
ans2 = (d * pre) % mod;
}
else {
ans1 = (a * pre) % mod;
ans2 = (b * pre) % mod;
}
cout << ans1 << " " << ans2 << endl;
}
}
1002
题意:
题解:
根据题意,可以发现a[n]的排列顺序对答案是不会产生影响的
比如 a={ 2, 3 },k = 1, 那么一种构造为b={1, 2},x=2;
现在改变a的顺序为{3,2},那么相应的b={2,1},x=2;
因此,不妨把a变为一个升序序列,为了使b的数字种类够多,可以在构造b时,往小处取值,并保证数字种类的不同。这样构造出来的b也是一个升序序列。
这里可以使用map,用于表示对于某种数字,当前可以使用的最小值。
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#define INF 0x3f3f3f3f
#define ll long long
const double pi = acos(-1.0);
using namespace std;
const ll mod = 998244353;
ll a[100005]; map<ll, ll> pos;
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t; cin >> t;
while (t--) {
int n; cin >> n;
ll k; cin >> k;
pos.clear();
for (int i = 0; i < n; i++) {
cin >> a[i];
pos[a[i]] = a[i] - k;//该数字初始可使用的最小值一定是 数字-k
}
ll ans = 0;
sort(a, a + n);
//a[0] = pos[a[0]];
pos[a[0]]++;//第一位的最小值选定,可选最小值加1
ans++;
for (int i = 1; i < n; i++) {
//如果当前数字的可选最小值比上个数字小,表明当前最小值不符合条件,必然和之前数字有重复
//因此要进行更新
if (pos[a[i]] < pos[a[i - 1]])pos[a[i]] = pos[a[i - 1]];
//在没超过k的范围内,数字种类可增加,并更新可选最小值
if (pos[a[i]] <= a[i] + k) {
pos[a[i]]++;
//a[i] = pos[a[i]] - 1;
ans++;
}
else {
//a[i] = pos[a[i]] - 1;
}
}
cout << ans << "\n";
}
}
1003
题意:
给一张无向完全图,每条边有一个颜色,为黑色或者白色。你初始在点 s 上,你每次可以从当前的点经过一条边走到另一个点上,并将走过的边的颜色翻转。你想要把图中所有的边全都变为黑色,要求最小化走过的边的条数,求这个最小值,或者判断无解。
题解:
首先,题中所给的图是完全图,则必然是简单图,且每两点之间都有边相连。
要把所有白边变为黑边,白边要经过奇数次,黑边变为黑边,黑边要经过偶数次。为了使次数减少,那么原题可变为所有白边都经过1次并且借助最少的黑边的方案。
我们可以先把所有白边单独构图,作为一个白边子图。这样你会得到几个仅有白边的连通分量。为了使所有白边能经过,每两个连通分量间用黑边连接。由于是完全图,这样的黑边是必然存在的。
黑边要经过两次,因此加入到白边图中就等价于一对白色重边。
这样其实就构造好了一张连通的白边子图,这张子图的总边数就是我们要的答案。
但这个子图不一定是能达到要求的。子图所有边都是要不重复经过一次的,那就要用欧拉定理来判断。
这里说明一下,之前加黑边的操作是为了更好理解。判断是否为欧拉图/半图,初始的白边图就行。因为黑边只会给节点的度数加2,这不影响节点的奇偶性。
如果这不是欧拉图/半图,可以判为不可行。如果是欧拉图,那一定要判断起点是否为孤立节点,如果孤立,则必须与白边子图间用黑边连接。如果是欧拉半图,要是起点的度数不是奇数,则判为不可行。
图利用并查集储存。
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <cstring>
#define INF 0x3f3f3f3f
#define ll long long
const double pi = acos(-1.0);
using namespace std;
const ll mod = 998244353;
int degree[1005];
//==========================================
int net1[100010];
int afind1(int i)
{
if (net1[i] == i)return i;
else return net1[i] = afind1(net1[i]);
}
void u1(int x, int y)
{
if (afind1(x) != afind1(y))net1[afind1(x)] = afind1(y);
else return;
}
//==============================================
int net2[100010];
int afind2(int i)
{
if (net2[i] == i)return i;
else return net2[i] = afind2(net2[i]);
}
void u2(int x, int y)
{
if (afind2(x) != afind2(y))net2[afind2(x)] = afind2(y);
else return;
}
//==============================================
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t; cin >> t;
while (t--) {
int n; cin >> n;
int str; cin >> str;
memset(degree, 0, sizeof degree);
for (int i = 0; i <= n; i++) {
net1[i] = net2[i] = i;
}
int whi = 0;
for (int i = 2; i <= n; i++) {
string a; cin >> a;
for (int j = 0; j < a.length(); j++) {
if (a[j] == '0') {
u1(i, j + 1);//构造白边子图。
whi++;//白边加1
degree[i]++; degree[j + 1]++;//对应节点度数加1
}
else {
u2(i, j + 1);//构造黑边子图,这个应该不需要,请忽略。
}
}
}
bool f = 1; int jie = 0;
for (int i = 1; i <= n; i++) {
if (degree[i] & 1)jie++;
}
if (!(jie == 0 || jie == 2))f = 0;//判断是否为欧拉图/半图
if (f) {
if (jie == 2) {
if (!(degree[str] & 1))f = 0;//半图且起点度数不为奇数,不可行
}
}
//cout << degree[1] << degree[2] << endl;
if (f) {
bool che[1005] = { 0 };
for (int i = 1; i <= n; i++) {
if (degree[i])che[afind1(i)] = 1;//孤立节点不判断为一个连通分量
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (che[i])cnt++;//连通分量计数
}
if (degree[str] == 0)cnt++;//如果起点孤立,起点也作为一个连通分量
//cout << whi << " :" << cnt << endl;
if (whi)whi += 2 * (cnt - 1);//白边数加所需的黑边数的两倍
cout << whi << "\n";
}
else {
cout << -1 << "\n";
}
}
}
the end。