E 数圆圈(规律 模拟)
Description:
定义f(x) = x里各位圆圈数量之和
现在给定x 对x进行k次f(x)操作 返回结果
0, 6, 9 = 1; 8 = 2;
Solution:
那我们考虑最坏的情况 那就是17位上面都是8 那也是17*2 会化成一个很小的数
我们就可以知道 对于一个x 我们可以通过数十次次迭代之内 将他变成01的循环态
于是我们模拟即可 (用字符串不会挂 但是用整数是会挂的 因为x可能是负数
Code:
LL get(LL x)
{
LL res = 0;
string s = to_string(x);
for(int i = 0; i < s.size(); i++)
{
if(s[i] == '8') res += 2;
if(s[i] == '0' || s[i] == '6' || s[i] == '9') res ++;
}
return res;
}
void solve()
{
LL x, k;
cin >> x >> k;
LL res = x;
while(k)
{
res = get(res);
k --;
if(res == 0 || res == 1) break;
}
if(res == 1 && k)
{
if(k & 1) res = 0;
else res = 1;
}
else if(res == 0 && k)
{
if(k & 1) res = 1;
else res = 0;
}
cout << res << endl;
}
F 旅行(图论)
Description:
给出一个图 然后询问一下点a在不在1到n的最短路径上
Solution:
方法1:用佛洛依德跑一遍 判断g[1] [a] + g[a] [n] ?= g[1] [n]
方法2:首先用迪杰斯特拉从1跑到n 得到从1为起点到各个点的最短路
然后再建反边 从n开始跑到1 得到以n为起点到各个点的最短路
Code:
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, n) for(int i = a; i < n; i++)
#define per(i, a, n) for(int i = n - 1; i >= a; i--)
#define fi first
#define se second
#define pb push_back
#define ios ios::sync_with_stdio(false);cin.tie(0);
#define endl '\n'
#define ms(x, y) memset(x, y, sizeof x)
typedef double db;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1010, M = 3010;
int n, m, Q;
int dis1[N];
int dis2[N];
bool st[N];
int h[N], e[M], val[M], nex[M], idx;
void add(int a, int b, int c)
{
e[idx] = b;
nex[idx] = h[a];
val[idx] = c;
h[a] = idx ++;
}
void dijkstra1()
{
ms(dis1, 0x3f);
priority_queue<PII, vector<PII>, greater<PII> > q;
q.push({0, 1});
dis1[1] = 0;
while(q.size())
{
PII t = q.top();
q.pop();
int id = t.se, dist = t.fi;
if(st[id]) continue;
st[id] = true;
for(int i = h[id]; i != -1; i = nex[i])
{
int j = e[i];
if(dis1[j] > val[i] + dist)
{
dis1[j] = val[i] + dist;
q.push({dis1[j], j});
}
}
}
}
void dijkstra2()
{
ms(dis2, 0x3f);
priority_queue<PII, vector<PII>, greater<PII> > q;
q.push({0, n});
dis2[n] = 0;
while(q.size())
{
PII t = q.top();
q.pop();
int id = t.se, dist = t.fi;
if(st[id]) continue;
st[id] = true;
for(int i = h[id]; i != -1; i = nex[i])
{
int j = e[i];
if(dis2[j] > val[i] + dist)
{
dis2[j] = val[i] + dist;
q.push({dis2[j], j});
}
}
}
}
int main()
{
ms(h, -1);
scanf("%d%d%d", &n, &m, &Q);
vector<int> u(m), v(m), c(m);
for(int i = 0; i < m; i++)
scanf("%d%d%d", &u[i], &v[i], &c[i]);
for(int i = 0; i < m; i++)
add(u[i], v[i], c[i]);
dijkstra1();
ms(h, -1);
ms(st, false);
idx = 0;
for(int i = 0; i < m; i++)
add(v[i], u[i], c[i]);
dijkstra2();
while(Q --)
{
int l;
scanf("%d", &l);
if(dis1[l] + dis2[l] == dis1[n])
puts("yes");
else puts("no");
}
return 0;
}
H 寻找最大差值(思维)
Description:
给定数组a 求a_j - a_i (且a_j > a_i) 的最大值 要求i < j 如果这个数不存在就输出-1
Solution:
一开始就各种二分 排序 各种想 就是想不到怎么写 那其实可以用On写过
我们倒序循环j来维护一个mx=max(a[j]) 然后用mx去减去比他更小的a[i] 这时候必定存在i < j
Code:
int a[100010];
void solve()
{
int n;
cin >> n;
rep(i, 1, n + 1)
cin >> a[i];
int mx = 0;
int res = -1;
per(i, n, 1)
{
mx = max(mx, a[i]);
if(mx - a[i] > 0) res = max(mx - a[i], res);
}
cout << res << '\n';
}
H Raksasa的轻功(读题)
Description:
给定数组a 可以从任一点开始找最长连续下降序列(可以向左或者向右)
Solution:
我们可以从两个端点向另一点扫 因为可以连续向左也可以连续向右
然后 我们就On求一下两次最长连续下降序列
还有一种做法就是从前扫到后 记录最长连续下降序列和最长连续上升序列(倒过来做)
Code:
int a[200010];
int main()
{
int n;
cin >> n;
rep(i, 1, n + 1)
cin >> a[i];
int up = 0, down = 0, res = 0;
rep(i, 1, n)
{
if(a[i] < a[i + 1]) res = max(res, ++ up), down = 0;
else if(a[i] > a[i + 1]) res = max(res, ++ down), up = 0;
else if(a[i] == a[i + 1]) up = 0, down = 0;
}
cout << res << endl;
}
L 函数求和(思维)
Description:
题目就是这个意思
∑
i
=
1
n
m
i
n
L
i
<
=
j
<
=
i
{
g
c
d
(
i
,
j
)
}
m
o
d
(
1
0
9
+
7
)
\sum_{i=1}^n min_{L_i<=j<=i}\left\{gcd(i,j)\right\}\ mod (10^9+7)
i=1∑nminLi<=j<=i{gcd(i,j)} mod(109+7)
Solution:
因为保证L_i <= j <= i 当i = L_i的时候 j只能选L_i这点我们是可以确认的
那如何取最小的gcd呢 那我们可以知道gcd(x, x - 1) = 1
所以 我们想到辣 当i != L_i的时候 我们直接选gcd(i, i - 1)就可以了
Code:
int a[10000010];
int main()
{
int n = read(), m = read();
for(int i = 1; i <= n; i++)
a[i] = read();
LL res = 0;
while(m --)
{
int x = read();
if(x == a[x]) res = (res + x) % mod;
else res = (res + 1) % mod;
}
cout << res;
return 0;
}
N Raksasa的数字(构造)
Description:
给定数组a 我们可以选择一个数x来对数组内的数全部进行异或
最后要使得数组和最小 当答案有多个x的时候 我们要选择小的x
Solution:
因为异或 让相同的变成0 不同的变成1 所以如果对于一位数来说
0多的话就让这一位异或0 1多的话就让这一位异或1
贪心地构造一下吧 就对于每个数 将其化为二进制格式 然后统计一下每一位是0的个数多还是1的个数多
那什么时候会出现多个x呢 就是当一位数上0和1个数相等 这个时候 异或0和1都是一样的结果 但是因为为了保证x最小 这时候要异或0
trick:用bitset可以减少时间复杂度
Code:
int cnt[32];//每一位1的数量
void solve()
{
vector<bitset<32>> a;
int n;
cin >> n;
rep(i, 0, n)
{
int x;
cin >> x;
bitset<32> bi(x); //把x化为01串
a.pb(bi);
}
ms(cnt, 0);
rep(i, 0, n)
{
rep(j, 0, 32)
cnt[j] += a[i][j];
}
LL res = 0;
rep(i, 0, 32)
{
if(cnt[i] > n / 2) //尽量小 所以0和1个数相等的时候不加上这一位
res += (1LL << i);
}
cout << res << endl;
}