Linking
题意:
给定一个数列
A
1
,
A
2
,
⋯
,
A
n
A_1,A_2,⋯,A_n
A1,A2,⋯,An,每次操作可以选择若干位置构成一个集合
B
1
,
B
2
,
⋯
,
B
m
(
B
i
∈
1
,
2
,
⋯
,
n
)
B_1,B_2,⋯,B_m(B_i∈{1,2,⋯,n})
B1,B2,⋯,Bm(Bi∈1,2,⋯,n),每个位置
A
B
i
A_{B_i}
ABi 减少
2
i
−
1
2^{i-1}
2i−1。其中,
B
i
,
B
j
Bi,Bj
Bi,Bj 可以为同一位置。
问,最少多少次操作能将数列中的所有值都变为 0 ?
(
1
≤
n
≤
1
0
5
,
1
≤
A
i
≤
1
0
9
)
\,(1\le n \le 10^5,\,1\le A_i \le 10^9)
(1≤n≤105,1≤Ai≤109)
样例:
3
5
1 2 3 4 5
2
1 4
1
7
3
3
1
思路:
每次操作都可以使若干值分别减去 1, 2, 4, 8...
,而对于一次操作来说,可以使得一个位置既减去
2
i
{2^i}
2i,又减去
2
j
{2^j}
2j,所以需要想到把所有二进制数从一个数中分离出来。
- 每次操作都要从 1 开始,所以为了所有的二进制数都要被减去,需要使得二进制数个数从低位到高位递减。
- 而为了使得操作次数最少,就要二进制数1的个数最少。
从后往前遍历,如果发现当前位置大于前一个位置了,就匀到前一个位置些(匀出的个数*=2),以保证前一个位置个数 >= 当前位置个数。
进行若干遍此操作,便能保证低位到高位递减,同时 1 的个数最小化。
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N], f[N];
int cnt[N];
signed main(){
cin >> T;
while(T--)
{
cin >> n;
for(int i=0;i<32;i++) cnt[i] = 0;
for(int i=1;i<=n;i++)
{
int x; cin >> x;
bitset<33> st(x);
for(int j=0;j<33;j++)
{
if(st[j]) cnt[j] ++;
}
}
int flag = 1;
while(flag)
{
flag = 0;
for(int i=32;i>0;i--)
{
if(cnt[i] > cnt[i-1])
{
flag = 1;
int cha = cnt[i] - cnt[i-1];
if(cha % 3 == 0)
cnt[i-1] += cha/3*2, cnt[i] -= cha/3;
else
cnt[i-1] += (cha/3+1)*2, cnt[i] -= cha/3+1;
}
}
}
cout << cnt[0] << endl;
}
return 0;
}
E. Power and Modulo
题意
给定 n 个数,依次是 n 个2进制数 %M 得到的。
判断 M 是否唯一。
n
(
1
≤
n
≤
1
0
5
)
,
A
1
,
A
2
,
⋯
,
A
n
(
0
≤
A
i
≤
1
0
9
)
n\,(1\le n \le 10^5),\ A_1, A_2, \cdots, A_n \,(0 \le A_i \le 10^9)
n(1≤n≤105), A1,A2,⋯,An(0≤Ai≤109)
思路
虽然 n 很大,但是 Ai 最大只有 1e9,所以当 n>32 时一定能找到取模的值。
首先找到第一个和二进制数不相等的位置,那么两数之差便是 M。
需要注意的是,当找到 M 后应直接break,重新从前往后遍历所有位置判断是否满足二进制数取模 M(因为可能后面找到 M 后,前面出现的数可能比 M 大,那么这种情况下数列就是不合法的,也找不到对应的 M)。
对于两个数相乘取模,先相乘再取模 与 先取模再相乘 是相等的。
所以找到 M 后,二进制数 x 就可以不断 乘2 之后取模,不会爆 long long。
Code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N];
signed main(){
cin >> T;
while(T--)
{
cin >> n;
int x = 1, flag = 0, ans = 0;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++)
{
if(i!=1) x *= 2;
if(a[i] != x){
flag = x - a[i];
break;
}
}
if(flag)
{
x = 1;
for(int i=2;i<=n;i++)
{
if(i!=1) x = x*2; //注意这里分开操作
x %= flag; //当i=1时x=1,也要对flag取模
if(a[i] != x){
ans = -1;
break;
}
}
}
if(ans == -1 || !flag) cout << -1 << endl;
else cout << flag << endl;
}
return 0;
}
坑点太多了。。