暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第四场)
本来想说这场放掉了,算了还是补了吧...
以下题解包括:
【1001】 思维 HDU-6614 AND Minimum Spanning Tree
http://acm.hdu.edu.cn/showproblem.php?pid=6614
需要建一颗树,使得边权值和最小,边权为两点的 “&” 值。
假设二进制位 101,那就和 2 连接;假设为 111,那就看一下有没有 8 这个点,没有的话就和点 1 连接。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 2e5+5;
int a[maxn];
int wei(int x) {
int ans = 0;
while(x) {
ans ++;
x /= 2;
}
return ans;
}
int main() {
int t;
scanf("%d", &t);
while(t--) {
int n;
scanf("%d", &n);
int x = wei(n);
int s = 0;
for(int i = 0; i < x; i++) {
if((n>>i) & 1) {
s++;
}
}
int ans = 0;
if(s == x) {
ans ++;
}
printf("%d\n", ans);
for(int i = 2; i <= n; i++) {
if(ans == 1 && i == n) {
printf("1\n");
break;
}
for(int j = 0; j < x; j++) {
if((i>>j) & 1) {
continue;
}
printf("%d%c", (1<<j), i==n?'\n':' ');
break;
}
}
}
return 0;
}
【1003】 思维 HDU-6616 Divide the Stones
http://acm.hdu.edu.cn/showproblem.php?pid=6616
给定重量 \(1\) 到 \(n\) 的 \(n\) 个石头,让你分成重量相等,数量也相等的 \(k\) 组,保证 \(k\) 是 \(n\) 的约数。输出具体的分配方案。
参考:https://www.cnblogs.com/isakovsky/p/11281662.html
首先,如果 \(1\) 到 \(n\) 之和不能整除 \(k\),那么一定不能分配;否则一定能。
设 \(m=n/k\)。\(m\) 是每组分到的石头块数。我们把n块石头排成这样 \(m*k\) 的矩阵,假设 12 块石头,分成 3 组。
- m为偶数,只要每次取头尾各 \(m/2\) 个即可。
- m为奇数,将后两列之和构造成从上到下递增1,然后剩下的奇数个,一行构造成递增,一行构造成递减。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int main() {
int t;
scanf("%d", &t);
while(t--) {
int n, k;
scanf("%d%d", &n, &k);
ll sum = 1ll*n*(n+1)/2;
if(k == 1) {
printf("yes\n");
for(int i = 1; i <= n; i++) {
printf("%d%c", i, i==n?'\n':' ');
}
}
else if(sum % k) {
printf("no\n");
}
else {
printf("yes\n");
if((n/k) % 2 == 0) {
int x = n / k;
int cnt = 0;
for(int i = 1; i <= n/2; i++) {
printf("%d %d", i, n-i+1);
cnt += 2;
if(cnt == x) {
printf("\n");
cnt = 0;
}
else {
printf(" ");
}
}
}
else {
int x = n / k;
int tot = 1 + 2*k - k/2;
int temp = 1;
for(int i = 1; i <= k; i++) {
for(int j = 1; j <= x-2; j++) {
if(j % 2 == 1) {
printf("%d ", n-(j-1)*k-i+1);
}
else {
printf("%d ", n-j*k+1+i-1);
}
}
printf("%d %d\n", temp, tot - temp);
tot ++;
temp += 2;
if(temp > k) {
temp = 2;
}
}
}
}
}
return 0;
}
【1007】 思维 HDU-6620 Just an Old Puzzle
http://acm.hdu.edu.cn/showproblem.php?pid=6620
给定一个数字拼图,问能否能复原成原来的样子(120步以内)。
经典的华容道问题变形。
定理:逆序数奇偶相同时,可以互相转化,逆序数奇偶不同,不能互相转化。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int a[20];
int main( ) {
int t;
scanf("%d", &t);
int flag, ans;
while(t--) {
ans = 0;
for(int i = 1; i <= 16; i++) {
scanf("%d", &a[i]);
if(a[i] == 0) {
flag = i / 4 + (i % 4 != 0);
}
else {
for(int j = 1; j < i; j++) {
if(a[j] == 0) {
continue;
}
if(a[j] > a[i]) {
ans++;
}
}
}
}
printf((4-flag) % 2 == ans % 2 ? "Yes\n" : "No\n");
}
return 0;
}
【1008】 主席树 HDU-6621 K-th Closest Distance
http://acm.hdu.edu.cn/showproblem.php?pid=6621
给定 \(n\) 个数,\(q\) 次查询,每次查询 \([l , r]\) 内, \(| a[i] - p |\) 第 \(k\) 大的数,且强制要求在线。其中 \(n \leq 1e^5 \ \ q \leq 1e^5\)
比赛时候一直想着主席树,一直没办法吧 k 这个大常数优化掉,就 T 了一整场。结果发现可以换一种存法就行了,或许应该叫权值主席树?QAQ
主席树的结点直接就是 \(1e^6\) 个值,不进行离散化,对每个值都单独统计出现的次数。二分查询 \((p-mid,p+mid)\) 中数的个数。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 5;
int n, m, cnt, new_n;
int a[maxn];
int root[maxn];
vector<int> v;
struct node {
int l, r, sum;
}T[maxn*40];
void init() {
v.clear();
cnt = 0;
}
inline void update(int l, int r, int &x, int y, int val) {
T[++cnt] = T[y];
T[cnt].sum ++;
x = cnt;
if(l == r) {
return ;
}
int mid = (l+r) / 2;
if(mid >= val) {
update(l, mid, T[x].l, T[y].l, val);
}
else {
update(mid+1, r, T[x].r, T[y].r, val);
}
}
inline int query(int L, int R, int l, int r, int x, int y) {
if(L <= l && r <= R) {
return T[y].sum - T[x].sum;
}
int mid = (l+r) / 2;
int ans = 0;
if(L <= mid) {
ans += query(L, R, l, mid, T[x].l, T[y].l);
}
if(R > mid) {
ans += query(L, R, mid+1, r, T[x].r, T[y].r);
}
return ans;
}
int main() {
int t;
scanf("%d", &t);
while(t--) {
init();
int MAX = 1000000;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {
update(1, MAX, root[i], root[i-1], 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 = l^ans;
r = r^ans;
p = p^ans;
k = k^ans;
int L = 0;
int R = MAX;
while(L <= R) {
int mid = (L+R) >> 1;
if(query(max(1, p-mid), min(MAX, p+mid), 1, MAX, root[l-1], root[r]) >= k) {
ans = mid;
R = mid - 1;
}
else {
L = mid + 1;
}
}
printf("%d\n", ans);
}
}
return 0;
}
【1010】 数学 HDU-6623 Minimal Power of Prime
http://acm.hdu.edu.cn/showproblem.php?pid=6623
给定一个数 \(n\),求它的素因子里最大的指数是多少。
考虑 \(n^{1/5}\) 范围内的素数,计算范围内的最小素数的次数是多少,然后除去这些素数,得到剩余的数字 \(m\),考虑 \(m\) 如果大于 10009(就是素数1~n^(1/5)范围内的素数,n的范围是(10009,1e18),此时已知最小素数时10009,所以最是最小素数的4次方,分别讨论m是否为素数的2,3,4次方即可,如果都不是,就是1次方)。要特判1。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e4+5;
int vis[maxn], pri[maxn];
int tot;
ll n;
void prime() {
for(int i = 2; i < maxn; i++) {
if(vis[i] == 0) {
pri[tot++] = i;
for(int j = i*i; j < maxn; j+=i) {
vis[j] = 1;
}
}
}
}
int check(ll x) {
int l = 0;
int r = 1000000;
while(l <= r) {
int mid = (l+r) >> 1;
if(1ll*mid*mid*mid == x) {
return 1;
}
if(1ll*mid*mid*mid > x) {
r = mid-1;
}
else{
l = mid+1;
}
}
return 0;
}
int main() {
prime();
int t;
scanf("%d", &t);
while(t--) {
scanf("%lld", &n);
if(n == 1) {
printf("0\n");
continue;
}
ll ans = n;
for(int i = 0; i < tot; i++) {
if(n == 1) {
break;
}
if(n % pri[i] == 0) {
int cnt = 0;
while(n % pri[i] == 0) {
cnt ++;
n /= pri[i];
}
ans = min(ans, 1ll*cnt);
}
}
if(n >= maxn) {
ll s1 = (ll)sqrt(n);
ll s2 = (ll)sqrt(s1);
if(s2*s2*s2*s2 == n) {
ans = min(ans, 4ll);
}
else if(s1*s1 == n) {
ans = min(ans, 2ll);
}
else if(check(n)) {
ans = min(ans, 3ll);
}
else {
ans = min(ans, 1ll);
}
}
printf("%lld\n", ans);
}
return 0;
}