2019 Multi-University Training Contest 4
题意:设(x)号结点与(y)号结点之间的边权为(x and y),求(n)个点的图的最小生成树。如果有多种答案,输出从2号点开始连接的节点编号字典序最小的答案。
题解:对于每个数,按最低位(0)的位置取反得到一个与之相连的数。如果大于(n)就连(1)。
#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
#define fopo freopen("out.txt", "w", stdout)
using namespace std;
typedef long long LL;
const int maxn = 2e5 + 5;
int T, n;
int get(int x) {
int res;
for (int i = 0; i <= 20; i++)
if (!((res = 1<<i) & x)) break;
return res > n ? 1 : res;
}
int a[maxn];
int main() {
scanf("%d", &T);
for (int ca = 1; ca <= T; ca++) {
scanf("%d", &n);
int ans = 0;
for (int i = 2; i <= n; i++)
a[i] = get(i), ans += i & a[i];
printf("%d
", ans);
for (int i = 2; i <= n; i++)
printf("%d%c", a[i], i == n ? '
' : ' ');
}
}
题意:给一个16数码棋盘,问能否变成终止态(1~15与空格按从上到下从左往右的顺序排好)。
题解:N数码问题可以通过两个状态的逆序对来判断是否相互可达。
- 若N为奇数,则判逆序对个数的奇偶性是否相同。
- 若N为偶数,则判逆序对个数之差与两个局面下空格所在行数之差的奇偶性是否相同。
若相同,则可达。
所以本题只要判断一下给的状态与终止态是否可达即可。
#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
#define fopo freopen("out.txt", "w", stdout)
using namespace std;
typedef long long LL;
int T;
int a[20];
int main() {
scanf("%d", &T);
for (int ca = 1; ca <= T; ca++) {
int tmp = 0, cnt = 0;
for (int i = 1; i <= 16; i++) {
scanf("%d", &a[i]);
if (a[i] == 0) {
tmp = (i-1)/4 + 1;
continue;
}
for (int j = 1; j < i; j++)
if (a[j] && a[i] < a[j]) cnt++;
}
printf(cnt%2 == tmp%2 ? "Yes
" : "No
");
}
}
题意:长度为(n)的序列,(m)次询问,每次查询区间([l, r])中,与(p)差的绝对值第(k)小的数,输出差值。
题解:建可持久化线段树。二分差值(mid),看区间内范围在([p-mid,p+mid])之间的数是否大于等于(k)。
#include <cstdio>
#include <algorithm>
#define fopi freopen("in.txt", "r", stdin)
#define fopo freopen("out.txt", "w", stdout)
using namespace std;
typedef long long LL;
const int maxn = 2e5 + 10;
const int M = maxn * 30;
int ls[M], rs[M], val[M], sum[M], root[M];
int cnt = 0, a[maxn], q[maxn], len;
void update(int &now, int l, int r, int x) {
ls[++cnt] = ls[now], rs[cnt] = rs[now];
sum[cnt] = sum[now]+1, now = cnt;
if (l == r) return;
int mid = (l+r) / 2;
if (x <= mid) update(ls[now], l, mid, x);
else update(rs[now], mid+1, r, x);
}
int query(int L, int R, int l, int r, int x, int y) {
if (x <= l && y >= r) return sum[R] - sum[L];
int mid = (l+r) / 2, res = 0;
if (x <= mid) res += query(ls[L], ls[R], l, mid, x, y);
if (y > mid) res += query(rs[L], rs[R], mid+1, r, x, y);
return res;
}
int solve(int l, int r, int p, int k) {
int L = 0, R = len, ans;
while(L <= R) {
int mid = (L+R) / 2, x = max(1, p-mid), y = min(p+mid, len);
int c = query(root[l-1], root[r], 1, len, x, y);
if (c >= k) ans = mid, R = mid-1;
else L = mid+1;
}
return ans;
}
int T, n, m;
int main() {
scanf("%d", &T);
for (int ca = 1; ca <= T; ca++) {
scanf("%d%d", &n, &m);
cnt = 0;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
len = 1000000;
for (int i = 1; i <= n; i++) {
root[i] = root[i-1];
update(root[i], 1, len, a[i]);
}
int ans = 0;
for (int i = 1; i <= m; i++) {
int l, r, p, k;
scanf("%d%d%d%d", &l, &r, &p, &k);
l ^= ans, r ^= ans, p ^= ans, k ^= ans;
printf("%d
", ans = solve(l, r, p, k));
}
}
}
题意:求(n)分解质因数后,所有质因子的次幂的最小值。((n<=1e18))
题解:可以只分解到(n^{1/5})。若问题的答案不是1,则分解后剩余部分的(n)只可能是四种情况:(p^4,p^3,p^2,p^2q^2)。逐次判断之即可。
#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
#define fopo freopen("out.txt", "w", stdout)
using namespace std;
typedef long long LL;
const int maxn = 10000 + 5;
const double eps = 1e-8;
int T, tot = 0;
LL n;
bool check(int x) {
for (int i = 2; i <= sqrt(x); i++)
if (x % i == 0) return false;
return true;
}
bool sgn(double a, double b) {
if (a - eps > b) return 1;
if (a + eps < b) return -1;
return 0;
}
int prime[maxn];
int main() {
for (int i = 2; i <= 4000; i++)
if (check(i)) prime[++tot] = i;
scanf("%d", &T);
for (int ca = 1; ca <= T; ca++) {
scanf("%lld", &n);
int ans = 100;
for (int i = 1; i <= tot && prime[i] <= n; i++) {
int res = 0;
while(n % prime[i] == 0)
n /= prime[i], res++;
if (res) ans = min(ans, res);
}
if (n == 1) {
printf("%d
", ans);
continue;
}
for (int i = 4; i >= 1; i--) {
LL x = round(pow(n, 1.0/i));
if (sgn(pow(x, i), n) == 0) {
ans = min(ans, i);
break;
}
}
printf("%d
", ans);
}
}