题解:P9333 [JOISC 2023 Day2] Council
anthonyaaabc · · 题解
提供一个不太一样的做法,没有使用高维前缀和之类的二进制处理的工具。
首先注意到
如果我们将那些恰好等于的提案状压成一个二进制数,则问题被转换成找到一个二进制数,使得两个数
然后我没有想到怎么使用任何二进制处理的方法解决这个问题。考虑为什么最基础的暴力很慢,每次添加一个人的时候相当于一次修改操作,我们每次查询的时候都得枚举
代码
#include<bits/stdc++.h>
using namespace std;
const int N=300005;
const int bk=(1<<10)-1;
int n,m;
int a[N][21];
int vl[(1<<10)+5][(1<<10)+5],p;
int num[N];
int val[N];
int ans[N];
int cnt[(1<<10)+5];
void insert(int num)
{
int half=num&bk;
int res=num>>10;
for(int i=0;i<1024;i++)
{
vl[i][res]=min(vl[i][res],cnt[half&i]);
}
}
int get(int x)
{
int half=x&bk;
int res=x>>10;
int val=1e9+7;
for(int i=0;i<1024;i++)
{
val=min(val,vl[half][i]+cnt[res&i]);
}
return val;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=0;i<1024;i++)
{
cnt[i]=__builtin_popcount(i);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
if(a[i][j])
{
num[j-1]++;
val[i]|=(1<<j-1);
}
}
}
m=20;
memset(vl,0x3f,sizeof(vl));
insert(val[1]);
int mid=n>>1;
for(int i=2;i<=n;i++)
{
int sum=0;
for(int j=0;j<m;j++)
{
if((val[i]>>j)&1)num[j]--;
}
int tmp=0;
for(int j=0;j<m;j++)
{
if(num[j]>=mid)sum++;
if(num[j]==mid)tmp|=(1<<j);
}
ans[i]=max(ans[i],sum-get(tmp));
for(int j=0;j<m;j++)
{
if((val[i]>>j)&1)num[j]++;
}
insert(val[i]);
}
memset(vl,0x3f,sizeof(vl));
insert(val[n]);
for(int i=n-1;i>=1;i--)
{
int sum=0;
for(int j=0;j<m;j++)
{
if((val[i]>>j)&1)num[j]--;
}
int tmp=0;
for(int j=0;j<m;j++)
{
if(num[j]>=mid)sum++;
if(num[j]==mid)tmp|=(1<<j);
}
ans[i]=max(ans[i],sum-get(tmp));
for(int j=0;j<m;j++)
{
if((val[i]>>j)&1)num[j]++;
}
insert(val[i]);
}
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<'\n';
}
}