C:
有n个字符串,排在一列,我们要求让这些字符串按字典序升序排列,对于一个字符串我们可以将它反转,有一个费用ci,求使升序排列的最小费用
dp+log len的比较大小
f【i】【0】表示不翻转,使前i个升序的最小费用
f【i】【1】表示反转,使前i个升序的最小费用
肯定是从f【i-1】【0】或【1】转移来的
只要判断大小就可以了
不过有个很关键的,文中说字符串的总长不超过100000;其实就是,就算用暴力的判断大小,均摊复杂度,也是线性的,也完全可以过,
然而我脑残的想写后缀数组,又怕麻烦,就写了字符串hash(不会的就用hash水,啦啦啦)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=100050;
const ll inf=1000000000000000;
ll f[N][2],c[N];
int sum[N],n,bb[N],m;
ull hs1[N],hs2[N],po[N];
char s[100000];
bool pan(int a,int k1,int b,int k2,int len)//if a.1~len == b.1~len return true
{
ull h1=0,h2=0;
if (k1==0)
h1=hs1[sum[a-1]+len]-hs1[sum[a-1]]*po[len];
else h1=hs2[sum[a]-len+1]-hs2[sum[a]+1]*po[len];
if (k2==0)
h2=hs1[sum[b-1]+len]-hs1[sum[b-1]]*po[len];
else h2=hs2[sum[b]-len+1]-hs2[sum[b]+1]*po[len];
return h1==h2;
}
bool pan(int a,int k1,int b,int k2)//if a>b return true;
{
int ans=0,l=0,r=min(sum[a]-sum[a-1],sum[b]-sum[b-1]),mid;
while (l<=r)
{
mid=(l+r)>>1;
if (pan(a,k1,b,k2,mid)) ans=mid,l=mid+1;
else r=mid-1;
}
if (sum[b]-sum[b-1]==ans) return true;
if (sum[a]-sum[a-1]==ans) return false;
if (k1==0&&k2==0)
{
if (bb[sum[a-1]+ans+1]>bb[sum[b-1]+ans+1]) return true;
else return false;
}
if (k1==0&&k2==1)
{
if (bb[sum[a-1]+ans+1]>bb[sum[b]-ans]) return true;
else return false;
}
if (k1==1&&k2==0)
{
if (bb[sum[a]-ans]>bb[sum[b-1]+ans+1]) return true;
else return false;
}
if (k1==1&&k2==1)
{
if (bb[sum[a]-ans]>bb[sum[b]-ans]) return true;
else return false;
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%I64d",&c[i]);
int len;
for (int i=1;i<=n;i++)
{
scanf("%s",s+1);
len=strlen(s+1);
for (int j=1;j<=len;j++)
bb[j+sum[i-1]]=s[j]-'a'+1;
sum[i]=sum[i-1]+len;
}
po[0]=1;
for (int i=1;i<=sum[n];i++) hs1[i]=hs1[i-1]*29+bb[i];
for (int i=sum[n];i>=1;i--) hs2[i]=hs2[i+1]*29+bb[i];
for (int i=1;i<N;i++) po[i]=po[i-1]*29;
bool o=false;
f[1][0]=0;f[1][1]=c[1];
for (int i=2;i<=n;i++)
{
f[i][0]=inf;
if (pan(i,0,i-1,0)) f[i][0]=min(f[i][0],f[i-1][0]);
if (pan(i,0,i-1,1)) f[i][0]=min(f[i][0],f[i-1][1]);
f[i][1]=inf;
if (pan(i,1,i-1,0)) f[i][1]=min(f[i][1],f[i-1][0]+c[i]);
if (pan(i,1,i-1,1)) f[i][1]=min(f[i][1],f[i-1][1]+c[i]);
if (f[i][0]==inf&&f[i][1]==inf)
{
o=true;
break;
}
}
if (o) printf("-1");else
printf("%I64d",min(f[n][0],f[n][1]));
return 0;
}
D:有一个多重集,支持插入一个数,删除一个数,给出一个数x,求集合中与它的最大异或值
突然发现,像求异或的问题,好多都通过异或Trie来求解,同时Trie还是算个数据结构,可以支持这各种操作
把每个数的二进制做为“字符串”,每一个都是32位,及以上,找最大时,就从上往下,贪心走当前位置和该数不相同的子节点,并加到答案里面
题解上说,像这样最大异或的题要开到32位,并且一定要开long long
注意开ll,有是10的9次方。。(注意在各种可能地方开ll)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
int q,n,tot;
int ch[5000005][2],val[5000005];
bool v[5000500];
void insert(ll x)
{
int k=0;
for (int i=31;i>=0;i--)
{
ll id=x&(1<<i);
if (id) id=1;else id=0;
if (ch[k][id]==0) ch[k][id]=++n;
k=ch[k][id];
val[k]++;
}
v[k]=true;
}
void del(ll x)
{
int k=0;
for (int i=31;i>=0;i--)
{
ll id=x&(1<<i);
if (id) id=1;else id=0;
val[ch[k][id]]--;
if (val[ch[k][id]]==0)
{
ch[k][id]=0;
break;
}else k=ch[k][id];
}
}
void work(ll x)
{
ll ans=0;
int k=0;
for (int i=31;i>=0;i--)
{
ans<<=1;
ll id=x&(1<<i);
if (id) id=1;else id=0;
if (ch[k][1-id]) ans=ans|1,k=ch[k][1-id];
else k=ch[k][id];
}
printf("%I64d\n",ans);
}
int main()
{
scanf("%d",&q);
char ch[6];
ll x;
insert(0);
while (q--)
{
scanf("%s%I64d",ch,&x);
switch(ch[0])
{
case '+':insert(x);break;
case '-':del(x);break;
case '?':work(x);break;
}
}
return 0;
}