题解:AT_arc220_e [ARC220E] popcount ≥ K
更好的阅读体验
我们需要解决这样一个问题:找到最小的,连续
那么可以二分。假设目前位于区间
- 若区间
[l, mid) 中存在连续至少n 个\bmod \space c 同余的数字 popcount 都\ge k ,那么就进入[l, mid) 求解。 - 若存在
n 个\bmod \space c 同余,且 popcount 都\ge k 的数字跨过mid ,且[l, mid) 区间内部无解,那么在此处找到答案,可以直接退出。 - 否则进入
[mid, r) 求解答案。
假设答案上界为
由于
我们假设
合并这样的两个五元组是简单的,在这里不再赘述。
首先假设我们已经求出了
- 那么如果
l 的 popcount\ge k ,那么区间中每个节点的 popcount 都将会\ge k 。所以这种情况下直接求出区间长度,然后将len, mx, pre, suf 全部变成区间长度返回就好了。 - 否则
[l, l+2^i) 中\bmod \space c = j 且 popcount\ge k 的答案可以由[0, 2^i) 中\bmod \space c = (j-l) \bmod c ,且 popcount\ge k - \operatorname{popcount}(l) 的答案推算出。因此这种情况下返回f_{i, (j-l) \bmod c, k - \operatorname{popcount}(l)} 。
那么有了这个我们同时可以解决
由于此时
我们重新回到上述的二分过程。由于余数未知,我们可以首先枚举余数
按照上述过程二分即可。
假设答案上界为
#include<bits/stdc++.h>
#define endl '\n'
#define N 100006
using namespace std;
using i64=long long;
int q; i64 ans[N];
struct Ask {int n,c,k;} ask[N];
struct Node {
i64 len,pre,suf,mx; int all;
Node():len(0),pre(0),suf(0),mx(0),all(1) {}
void set() {len=pre=suf=mx=all=1;}
friend Node operator +(Node x,Node y)
{
Node ret;
ret.len=x.len+y.len,ret.all=x.all&y.all;
ret.pre=x.all?x.len+y.pre:x.pre;
ret.suf=y.all?x.suf+y.len:y.suf;
ret.mx=max({x.mx,y.mx,x.suf+y.pre});
return ret;
}
} f[60][36][60];
i64 calc(i64 r,int c,int k) {return r<k?0:(r-k)/c+1;}
i64 calc(i64 l,i64 r,int c,int k) {return calc(r,c,k)-calc(l-1,c,k);}
i64 get(i64 r,i64 x,int c,int k) {return k+(r-k)/c*c-(x-1)*c;}
Node query(int c,i64 st,int i,int j,int k)
{
i64 l=st,r=st+(1ll<<i)-1;
int pc=__builtin_popcountll(st);
if(!calc(l,r,c,j))return Node();
if(pc>=k)
{
Node ret;
ret.len=ret.pre=ret.suf=ret.mx=calc(l,r,c,j),ret.all=1;
return ret;
}
return f[i][((j-st+c)%c+c)%c][k-pc];
}
main()
{
scanf("%d",&q);
for(int i=1;i<=q;i++)
scanf("%d%d%d",&ask[i].n,&ask[i].c,&ask[i].k);
for(int c=1;c<=30;c++)
{
for(int i=0;i<60;i++)
for(int j=0;j<36;j++)
for(int k=0;k<60;k++)f[i][j][k]=Node();
for(int j=0;j<60;j++)
f[0][0][j].len=1,f[0][0][j].all=0;
f[0][0][0].set();
for(int i=0;i<59;i++)
for(int j=0;j<c;j++)
{
for(int k=0;k<=i+1;k++)
f[i+1][j][k]=f[i][j][k]+query(c,1ll<<i,i,j,k);
for(int k=i+2;k<60;k++)
{
f[i+1][j][k].len=calc(0,(1ll<<i+1)-1,c,j);
if(f[i+1][j][k].len)f[i+1][j][k].all=0;
}
}
for(int i=1;i<=q;i++)if(ask[i].c==c)
{
if(ask[i].n==1) {ans[i]=(1ll<<ask[i].k)-1; continue;}
i64 l=0,r=1ll<<60,b=60;
for(;;b--)
{
i64 mid=l+(1ll<<b-1);
int flag=0;
for(int x=0;x<c;x++)
if(query(c,l,b-1,x,ask[i].k).mx>=ask[i].n)flag=1;
if(flag) {r=mid; continue;}
i64 res=2e18;
for(int x=0;x<c;x++)
{
i64 l_suf=query(c,l,b-1,x,ask[i].k).suf;
i64 r_pre=query(c,mid,b-1,x,ask[i].k).pre;
if(l_suf+r_pre>=ask[i].n)
flag=1,res=min(res,get(mid-1,l_suf,c,x));
}
if(flag) {ans[i]=res; break;}
l=mid;
}
}
}
for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
return 0;
}