[ARC086F] Shift and Decrement 题解
Solution
一个简易的贪心想法是我们肯定是对于一个相同的序列求出操作到它的最小操作次数,看能否 \(\le K\)。
注意到我们在第 \(x\) 次A操作后进行 \(-1\) 操作相当于对于一开始进行 \(-2^x\)。
另外可以注意到的是,当我们进行了一次A操作之后如果进行 \(\ge 2\) 次操作,我们不如放在下一次A操作后面(除非是最后一次A操作),这样操作次数更少。假设我们使用了 \(k\) 次A操作,我们设 \(b_x\) 表示第 \(x\) 次操作后是否用B操作,设 \(P=\sum_{x}^{k-1} b_x2^x\),\(t\) 表示最后一次A操作后面用了多少次B操作,那么我们的操作相当于,对于 \(a_i\to a_i-P\),然后 \(a_i\to \lfloor\frac{a_i}{2^k}\rfloor\),然后 \(a_i\to a_i-t\)。
我们不妨先忽略掉最后一步,只考虑前面两步。可以发现的事情是,当 \(a_i\mod 2^k\le P\) 的时候,\(a_i\) 最后等于 \(\lfloor\frac{a_i}{2^k}\rfloor-1\),否则等于 \(\lfloor\frac{a_i}{2^k}\rfloor\)。那么我们把 \(a_i\mod 2^k\) 拿出来排序,假设是 \(c_{1,2,...,n}\),那么当 \(P\in (c_{i-1},c_i]\),可以看出不考虑步骤 \(3\) 的话序列是相同的,而我们考虑步骤 \(3\) 的话,假设 \(q=\min_{v\in (c_{i-1},c_i]} \text{popcount}(v)\),那么我们显然至多可以操作 \(K-k-q\) 次。那么我们作差分数组,相当于首项可以取在区间,那么对于差分数组相同的对于首项的区间求个并集之和即可。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define mod 1000000007
#define int long long
#define MAXN 205
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,K,tot,a[MAXN],b[MAXN],c[MAXN];
struct node{
int fir,ned;vector <int> S;
bool operator != (const node &p)const{
for (Int i = 1;i < n;++ i) if (S[i] != p.S[i]) return 1;
return 0;
}
bool operator < (const node &p){
for (Int i = 1;i < n;++ i)
if (S[i] != p.S[i]) return S[i] < p.S[i];
}
}s[MAXN * 65];
#define pii pair<int,int>
#define se second
#define fi first
set <pii> Sta;
int getall (){//求出区间并集
int ans = 0,L = 0,R = -1;
for (pii it : Sta){
if (it.fi > R) ans += R - L + 1,L = it.fi;
R = it.se;
}
return ans + R - L + 1;
}
int minpop (int L,int R){
if (L == -1) return 0;
int res = 0;
for (Int i = 60;~i;-- i)
if ((L >> i & 1) == (R >> i & 1)) res += (L >> i & 1);
else{
res ++;
break;
}
return res;
}
signed main(){
read (n,K);
for (Int x = 1;x <= n;++ x) read (a[x]);
sort (a + 1,a + n + 1);
for (Int k = 60;~k;-- k){
for (Int i = 1;i <= n;++ i) b[i] = a[i] & ((1ll << k) - 1);
sort (b + 1,b + n + 1),b[0] = -1;
for (Int i = 1;i <= n;++ i)
if (b[i - 1] != b[i] && b[i] <= a[1]){
s[++ tot].ned = k + minpop (b[i - 1],b[i]),s[tot].S.resize (n);
for (Int t = 1;t <= n;++ t) c[t] = a[t] - b[i] >> k;
s[tot].fir = c[1];
for (Int t = 1;t < n;++ t) s[tot].S[t] = c[t + 1] - c[t];
}
}
sort (s + 1,s + tot + 1);
int ans = 0;
for (Int i = 1;i <= tot;++ i){
if (i > 1 && s[i] != s[i - 1]) (ans += getall ()) %= mod,Sta.clear ();
if (s[i].ned <= K) Sta.insert ({max (s[i].fir - (K - s[i].ned),0ll),s[i].fir});
}
(ans += getall ()) %= mod,write (ans),putchar ('\n');
return 0;
}