题解:P17293 [Algo Beat Contest 013 & MSOI R2] 好朋友
lailai0916 · · 题解
题意简述
构造
解题思路
先把三元组贡献改写为数对贡献。固定无序下标对
因此,若
逐个二进制位考虑。若某一位在
将某一位的全部数位取反,会把
令
用动态位集保存状态。令
转移只能来自上一层,保证每一位恰好选择一次。保存所有层后,从
还需保证每个数不超过
前端非零元素恰为
最后确定动态位集的大小。若背包有解,
所以
又有
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;
const int N=1000005;
const int M=520005;
int a[N];
ull f[21][(M+63)/64];
bool get(int i,int x)
{
return f[i][x>>6]>>(x&63)&1;
}
void shift(int i,int x,int w)
{
int q=x>>6,r=x&63;
for(int j=0;j+q<w;j++)
{
f[i][j+q]|=f[i-1][j]<<r;
if(r&&j+q+1<w)f[i][j+q+1]|=f[i-1][j]>>(64-r);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,k;
cin>>n>>m>>k;
if(k%(6*n))
{
cout<<-1<<'\n';
return 0;
}
k/=6*n;
int b=0;
for(int i=m;i;i>>=1)b++;
int h=n/2;
ll mx=1LL*b*h*(n-h);
if(mx<k)
{
cout<<-1<<'\n';
return 0;
}
int w=(k>>6)+1;
f[0][0]=1;
for(int i=1;i<=b;i++)
{
for(int j=0;j<w;j++)f[i][j]=f[i-1][j];
for(int j=1;j<=h;j++)
{
ll x=1LL*j*(n-j);
if(x>k)break;
shift(i,(int)x,w);
}
}
if(!get(b,k))
{
cout<<-1<<'\n';
return 0;
}
int c[25]={};
int sum=k;
for(int i=b;i>=1;i--)
{
for(int j=0;j<=h;j++)
{
ll x=1LL*j*(n-j);
if(x>sum)break;
if(get(i-1,sum-x))
{
c[i-1]=j;
sum-=x;
break;
}
}
}
for(int i=1;i<=c[b-1];i++)a[i]=1<<(b-1);
for(int i=0;i<b-1;i++)for(int j=1;j<=c[i];j++)a[n-j+1]|=1<<i;
for(int i=1;i<=n;i++)cout<<a[i]<<(i==n?'\n':' ');
return 0;
}