思路来源
https://www.cnblogs.com/mooleetzi/p/11859365.html
E The Contest(dp)
第一个人有k1张牌,第二个人有k2张牌,第三个人有k3张牌(1<=k1,k2,k3<=2e5,k1+k2+k3<=2e5)
n=k1+k2+k3,n张牌构成1到n的一个排列,问最少交换多少次,
使得第一个人手里的牌对应一个1到i的前缀,
第三个人手里的牌对应一个j到n的后缀,
第二个人的牌为[i+1,j],允许所有牌只掌握在一个人或两个人手中
题解
pos[i]:记录下i这个值在哪个人手中
增序访问1到n,相当于把n个数分成三个区间,类似区间dp
区间dp是判断当前这个数是否新开一段,这里是判断当前这个数在第几段
dp[N][3],代表第i个数放在第j个人手中
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int INF=0x3f3f3f3f;
//dp[i][j]:��i��ֵ ���ڵ�j������
//���� ö�ٷֽ�������dp �ֳ�����
int dp[N][3],pos[N],v;
int n,k[3];
int main()
{
memset(dp,INF,sizeof dp);
for(int i=0;i<3;++i)
{
scanf("%d",&k[i]);
n+=k[i];
}
for(int i=0;i<3;++i)
{
for(int j=0;j<k[i];++j)
{
scanf("%d",&v);
pos[v]=i;
}
dp[0][i]=0;
}
for(int i=1;i<=n;++i)
{
for(int j=0;j<3;++j)//���
{
for(int l=0;l<=j;++l)//ǰ��
{
dp[i][j]=min(dp[i][j],dp[i-1][l]+(pos[i]!=j));
}
}
}
int ans=INF;
for(int i=0;i<3;++i)
ans=min(ans,dp[n][i]);
printf("%d\n",ans);
return 0;
}
F Make Them Similar(折半枚举+hash)
给你n(2<=n<=100)个数,第i个数ai(ai<2^30),
判断是否存在一个自然数x,令bi=ai异或x,
每个bi在二进制表示下中1的个数都相同
能的话输出x,否则输出-1
题解
①最暴力的做法,
考虑最终结果,bi在二进制表示下中1的个数为sum,
sum从0到30,枚举sum,对于每个sum,折半枚举前15位和后15位
相当于判断前15位的数组c[]={1,2,3,4,5}和后15位数组d[]={5,4,3,2,1}是否和相等
即判断对于当前数组{a,b,c,d},是否存在{sum-a,sum-b,sum-c,sum-d}
对于数组的hash,可以用map对vector哈希,
由于c[]和d[]中元素只会在0到15之间,所以sum-a只会在0到30之间,
可以用31和2^64次方(自然溢出)双哈希
②判断a[0]+b[0]==a[1]+b[1]+...=a[n]+b[n]=sum
即a[0]-a[1]=b[1]-b[0],a[0]-a[2]=b[2]-b[0]
因此,构造两个序列,一个是a[0]减a[i]的数组,一个是b[i]减b[0]的数组
无需知道sum,只需判断这两个数组相同即可,故不用枚举sum
减的值的范围[-15,15],加偏移量15之后[0,30]
此外,前一发代码写的常数比较大,可以用数组预处理出1<<15内的值二进制位中1的个数,
用空间换时间,干掉pop_count的一个log,
但其实此题复杂度较为玄学,或许是数据较弱
代码1(暴力)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=105;
int low,high,ans,sum;
int n,a[N],b[N],c[N],now;
unordered_map<ull,int>q;
void hs(int d[],int len,int x)
{
ull now=0;
for(int i=0;i<len;++i)
now=now*31+d[i];
q[now]=x;
}
int ok(int d[],int len,int x)
{
ull now=0;
int v;
for(int i=0;i<len;++i)
{
v=sum-d[i];
if(v<0)return -1;
now=now*31+v;
}
if(q.count(now))return x|q[now];
return -1;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;++i)
scanf("%d",&a[i]);
int up=1<<15;
for(int i=0;i<up;++i)
{
for(int j=0;j<n;++j)
{
low=a[j]&(up-1);
c[j]=__builtin_popcount(low^i);
}
hs(c,n,i);
}
ans=-1;
for(sum=0;sum<=30;++sum)
{
for(int i=0;i<up;++i)
{
for(int j=0;j<n;++j)
{
high=a[j]&(~(up-1));
c[j]=__builtin_popcount(high^(i<<15));
}
ans=ok(c,n,i<<15);
if(~ans)break;
}
if(~ans)break;
}
printf("%d\n",ans);
return 0;
}
代码2(优雅的暴力)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=105;
int low,high,ans,sum;
int cnt[1<<15];
int n,a[N],b[N],c[N],now;
unordered_map<ull,int>q;
void hs(int d[],int len,int x)
{
ull now=0;
for(int i=0;i<len;++i)
now=now*31+(d[i]+15);
q[now]=x;
}
int ok(int d[],int len,int x)
{
ull now=0;
for(int i=0;i<len;++i)
now=now*31+(d[i]+15);
if(q.count(now))return x|q[now];
return -1;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;++i)
scanf("%d",&a[i]);
int up=1<<15;
for(int i=0;i<up;++i)
cnt[i]=cnt[i&(i-1)]+1;
for(int i=0;i<up;++i)
{
for(int j=0;j<n;++j)
{
low=a[j]&(up-1);
c[j]=cnt[low^i];
}
for(int j=n-1;j>=0;--j)
{
c[j]=c[0]-c[j];
}
hs(c,n,i);
}
ans=-1;
for(int i=0;i<up;++i)
{
for(int j=0;j<n;++j)
{
high=a[j]&(~(up-1));
c[j]=cnt[(high^(i<<15))>>15];
}
for(int j=n-1;j>=0;--j)
{
c[j]=c[j]-c[0];
}
ans=ok(c,n,i<<15);
if(~ans)break;
}
printf("%d\n",ans);
return 0;
}