题意:你有一个完整的图,有N个顶点,从1到N。顶点x和顶点y (1<=x, y<=N, x!=y)之间的边的权值就是x和y的位和(AND)。你现在要求这个图的最小生成树每个点个连向哪个点
题解:为了让权值最小,肯定想让每一位都为0,假设某个数x的最低为的0的位置在i为,那么x & (1<<i) == 0。如果(1<<i)不在N的范围内,那么他至少要有一位AND后为1,那么不如把它和1连边。我们知道lowbit可以求最低为的1,那么按位取反在lowbit就是最低为0的位置
#include <bits/stdc++.h>
using namespace std;
int lowbit(int x){
return x & (-x);
}
int ans[200005],sum;
int main()
{
int T;
cin >> T;
while (T--){
int n;
scanf("%d",&n);
sum = 0;
for (int i = 2; i <= n; i++){
ans[i] = lowbit(~i);
if (ans[i] > n) ans[i] = 1;
sum += ans[i] & i;
}
printf("%d\n", sum);
for (int i = 2; i<= n;i++){
if (i == 2) printf("%d", ans[i]);
else printf(" %d", ans[i]);
}
printf("\n");
}
return 0;
}
C - Divide the Stones
题意:你有n堆石子,第i堆石子的重量是i,要求分成k组,是每组的重量相等
题解:如果石子的总重不是k的倍数,则不能分;否则可以分,且每堆n/k个石子
如果是偶数,则从前往后,在从后往前交替放石子就可以了
如果是奇数,则先把前1 ~
个石子分成k组,然后
1 ~ 怎么分,假设k = 9, 然后自己找规律吧
19 14 9
20 15 7
21 16 5
22 17 3
23 18 1
24 10 8
25 11 6
26 12 4
27 13 2
#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define FOR(I,S,T) for(int I=(S);I<=(T);I++)
using namespace std;
const int maxn = 1e5 +2;
typedef long long ll;
vector<int> v[maxn];
int main() {
int T;
cin >> T;
while (T--){
ll n, k;
scanf("%lld%lld",&n,&k);
for (int i = 0; i <= n; i++) v[i].clear();
if (k == 1){
puts("yes");
FOR(i,1,n) printf("%d%c", i, i == n?'\n':' ');
continue;
}
if ((n == k) || (((n + 1) * n / 2 % k) != 0)){
puts("no");
continue;
}
puts("yes");
if ((n / k) % 2ll== 0){
ll l = 1, r = n, d = n / k / 2;
FOR(i, 1, k) FOR(j, 1, d){
printf("%d %d",l++, r--);
if (j == d) putchar('\n');
else putchar(' ');
}
}
else {
FOR(i, 2 * k +1, 3 * k) v[i-2*k].push_back(i);
FOR(i, k +1, k * 3 / 2) v[i-k+(k+1)/2].push_back(i);
FOR(i, k * 3 / 2 + 1, 2 * k) v[i-k*3/2].push_back(i);
int tmp = k + 1;
FOR(i, 1, (k + 1)/2) v[i].push_back(tmp - i), tmp--;
tmp = k * 3 / 2 + 1;
FOR(i, (k + 1) / 2 + 1, k) v[i].push_back(tmp - i), tmp--;
int l = 3 * k + 1, r = n, d = (n/k - 3) / 2;
FOR(i, 1, k) FOR(j, 1, d) v[i].push_back(l++), v[i].push_back(r--);
}
for (int i = 1; i <= k; i++){
for (int j = 0; j < (int)v[i].size(); j++){
printf("%d", v[i][j]);
if (j == (int)(v[i].size()) - 1) putchar('\n');
else putchar(' ');
}
}
}
return 0;
}
题意:给出一个数字华容道,问能否还原
题解:可以发现相邻两个方格是可以移动的,所以按照蛇皮型或者环绕行把原数排列下来,因为相邻两个是无法交换的,所以逆序对是奇数数可以还原,否则不能还原
#include <bits/stdc++.h>
using namespace std;
vector<int> v;
int a[4][4];
int main()
{
int T;
cin >> T;
while (T--){
v.clear();
for (int i = 0; i <4 ;i++)
for (int j = 0; j < 4; j++){
scanf("%d", &a[i][j]);
}
for (int i = 0;i < 4; i++) v.push_back(a[0][i]);
for (int i = 3;i >= 0; i--) v.push_back(a[1][i]);
for (int i = 0;i < 4; i++) v.push_back(a[2][i]);
for (int i = 3;i >= 0; i--) v.push_back(a[3][i]);
int sum = 0;
for (int i = 0; i < 16; i++){
if (v[i] == 0) continue;
for (int j = i + 1; j < 16; j++){
//cout << v[i] << " " << v[j] << endl;
if (v[j] == 0) continue;
if (v[i] > v[j]) sum++;
}
}
if (sum & 1) printf("Yes\n");
else printf("No\n");
}
return 0;
}
H - K-th Closest Distance
题意:给出一个序列,问序列的一个区间的第k靠近p的数是是什么(强制在线)
题解:二分+主席树
主席树可以用来求小于于k大的数的个数,我们二分[p-x, p+x]中的x,就可以知道[p -x, p+x]中的数的个数,如果个数大于k则缩小x,否则扩大x
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 2;
const int maxt = 1e6 + 2;
struct node{
int l, r, sum, mx;
}M1[maxt * 50];
int a[maxn], root[maxn], cnt, n, m , bound;
void update(int l, int r, int &x, int y, int pos){
M1[++cnt] = M1[y];
M1[cnt].sum++;
x = cnt;
if (l == r) return;
int mid = (l + r) / 2;
if (pos <= mid) update(l, mid, M1[x].l, M1[y].l, pos);
else update(mid + 1, r, M1[x].r, M1[y].r, pos);
}
int query1(int l, int r, int x, int y, int k){//大于等于k的数量
if (k <= l) return M1[y].sum - M1[x].sum;
int mid = (l + r) / 2;
if (k <= mid) return M1[M1[y].r].sum - M1[M1[x].r].sum + query1(l, mid, M1[x].l, M1[y].l, k);
else return query1(mid+1, r, M1[x].r, M1[y].r, k);
}
int query3(int l, int r, int x, int y, int k){ //小于等于k的数量
if (k >= r) return M1[y].sum - M1[x].sum;
int mid = (l + r) / 2;
if (k <= mid) return query3(l, mid, M1[x].l, M1[y].l, k);
else return M1[M1[y].l].sum - M1[M1[x].l].sum + query3(mid+1, r, M1[x].r, M1[y].r, k);
}
int judge(int p, int x, int l, int r){
int tmp = query1(1, bound, root[l - 1], root[r], p - x);
tmp = r - l + 1 - tmp; // 小于p-x的数量
int tmp2 = query3(1, bound, root[l - 1], root[r], p + x);//小于等于p+x的数量
return tmp2 - tmp;
}
int main()
{
int T;
cin >> T;
int X = 0;
while (T--){
bound = 0;
scanf("%d%d", &n, &m);
for (int i = 0; i <= n+1; i++) a[i] = root[i] = 0;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), bound = max(bound, a[i]);
for (int i = 1; i <= n; i++) update(1, bound, root[i], root[i-1], a[i]);
X = 0;
for (int i = 1;i <= m;i++){
int x, y, p, k;
scanf("%d%d%d%d", &x, &y, &p, &k);
x ^= X;y ^= X; p ^= X;k ^= X;
int ll =0, rr = bound, ans = 0;
while (ll <= rr){
int mid = (ll + rr) / 2;
if (judge(p, mid, x, y) >= k){
ans = mid;
rr = mid - 1;
}else ll = mid + 1;
}
X = ans ;
printf("%d\n", X);
}
}
return 0;
}
J - Minimal Power of Prime
题意:给出一个n,如果素因数分解的幂次的最小值
题解:公式化 ,求
素因子分解内的所有素数,得到一个ans。如果还有素数大于
,那么他的指数最大是4,如果他能开4次根号ans=min{ans, 4},如果能开3次根号,ans=min{ans, 3}, 如果能开根号,ans= min{ans, 2},如果不能开根号ans=min{ans, 1}
#include <bits/stdc++.h>
#define FOR(I,S,T) for(int I=(S);I<=(T);I++)
#define pb push_back
#define mp make_pair
using namespace std;
typedef long long ll;
const int maxn = 4000;
const int inf = 0x3f3f3f3f;
vector <int> p;
int vis[maxn + 10];
int cnt = 0;
void pri(){
for (int i = 2; i <= maxn; i++){
if (!vis[i]) p.pb(i);
for (int j = 0; j < p.size() && i * p[j] <= maxn; j++){
vis[i * p[j]] = 1;
if (i % p[j] == 0) break;
}
}
}
int main()
{
pri();
int T;
scanf("%d", &T);
while (T--){
ll n;
scanf("%lld", &n);
ll k = n, cnt = 0, ans = inf;
for (int i = 0; i < p.size(); i++){
if (n % p[i] == 0) cnt= 0;
else continue;
while (n % p[i] == 0) {
cnt++;
n /= p[i];
}
ans = min(cnt, ans);
}
ll tmp = (ll)pow(n, 1.0/4);
if (n == 1){
printf("%lld\n", ans);
continue;
}
if (tmp*tmp*tmp*tmp== n || (tmp-1)*(tmp-1)*(tmp-1)*(tmp-1) == n || (tmp+1)*(tmp+1)*(tmp+1)*(tmp+1) == n){
ans= min(ans, 4ll);
printf("%lld\n", ans);
continue;
}
tmp = (ll)pow(n,1.0/3);
if (tmp*tmp*tmp == n || (tmp-1)*(tmp-1)*(tmp-1) == n || (tmp+1)*(tmp+1)*(tmp+1) == n) {
ans= min(ans, 3ll);
printf("%lld\n", ans);
continue;
}
tmp = (ll)pow(n, 1.0/2);
if (tmp*tmp == n || (tmp-1)*(tmp-1) == n || (tmp+1)*(tmp+1) == n) {
ans= min(ans, 2ll);
printf("%lld\n", ans);
continue;
}
ans= min(ans, 1ll);
printf("%lld\n", ans);
}
return 0;
}