复习时食用,会比较简略。
原理就不讲了,还不会字典树的先下车吧。
目录
#10049. 「一本通 2.3 例 1」Phone List
#10050. 「一本通 2.3 例 2」The XOR Largest Pair
#10051. 「一本通 2.3 例 3」Nikitosh 和异或
#10052. 「一本通 2.3 练习 1」Immediate Decodability
#10053. 「一本通 2.3 练习 2」L 语言
#10054. 「一本通 2.3 练习 3」Secret Message 秘密信息
#2012. 「SCOI2016」背单词
#10056. 「一本通 2.3 练习 5」The XOR-longest Path
#10049. 「一本通 2.3 例 1」Phone List
#10050. 「一本通 2.3 例 2」The XOR Largest Pair
题目
题目大意
在给定的N个整数中选出两个进行异或运算。
得到的结果最大是多少?
对于100%的数据,1<=n<=10^5,0<=ai<2^31。
题目分析
百度科普:异或即两个输入相同时为0,不同则为1。
首首先化成二进制建树。
为了让异或值max当然是要01反着跑,即一边跑向1另一边跑向0。
化为二进制后第i位不同则:ans+=2^(i-1)。
每插入一个数就跑一遍,要么你建完树再跑也行。
建树从高位到低位。废话肯定先捡大便宜再捡小便宜。
代码
#include
#include
#include
using namespace std;
int n,len=0;
long long a[100010],ans=0,biao=1;
struct node{int son[10];}tr[3100010];
void bt(long long aa)
{
int now=0,x,d=0,k[40];
long long kkk=biao,aans=0;
for(int i=1;i<=32;i++)
{
if(aa>=kkk) x=1,aa-=kkk;
else x=0;
if(tr[now].son[x]==0)
{
len++,tr[now].son[x]=len,now=len;
tr[now].son[0]=tr[now].son[1]=0;
}
else now=tr[now].son[x];
d++; k[d]=x;//k存这个数化为二进制每一位上的数
kkk/=2;
}
now=0; kkk=biao;
for(int i=1;i<=32;i++)
{
//是1往0跑,是0往1跑
if(tr[now].son[1-k[i]]!=0)
{
aans+=kkk;
now=tr[now].son[1-k[i]];
}
else now=tr[now].son[k[i]];//条件不允许你乱跑
kkk/=2;
}
ans=max(ans,aans);
}
int main()
{
scanf("%d",&n);
tr[0].son[0]=tr[0].son[1]=0;
for(int i=1;i<=31;i++) biao*=2;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
bt(a[i]);
}
printf("%lld",ans);
return 0;
}
#10051. 「一本通 2.3 例 3」Nikitosh 和异或
题目
题目大意
给定一个含N个元素的数组A,下标从1开始。
找出式子的最大值:(A[l_1]⨁A[l_1+1]⨁…⨁A[r_1])+(A[l_2]⨁A[l_2+1]⨁…⨁A[r_2])。
其中1≤l_1≤r_1
对于100%的数据,2<=n<=4*10^5,0<=ai<=10^9。
题目分析
首先记录异或前缀和s[i]=a[1]⊕a[2]⊕a[3]...⊕a[i]。
设l[i]为以i结尾的区间中,异或值的最大值。
因为异或有x⊕x=0的性质,所以区间[l,r]的异或值
=a[l]⊕a[l+1]⊕...⊕a[r]
=(a[1]⊕a[2]⊕a[3]...⊕a[l−1])⊕(a[1]⊕a[2]⊕a[3]...⊕a[r])
=s[l−1]⊕s[r]
所以求l[i],转化为找j
以上引自:https://www.cnblogs.com/qwerta/p/9822368.html 代码有点奇怪??? %%%
l_1和r_1正着找,得出前半段得max值。
l_2和r_2反正再找一遍,得出后半段得max值。道理等同于以上。即
首先记录异或前缀和s[i]=a[n]⊕a[n-1]⊕a[n-2]...⊕a[1]。
设l[i]为以i开头的区间中,异或值的最大值。
因为异或有x⊕x=0的性质,所以区间[l,r]的异或值
=a[l]⊕a[l+1]⊕...⊕a[r]
=(a[l]⊕a[l+1]⊕a[l+2]...⊕a[n])⊕(a[r+1]⊕a[r+2]⊕a[r+3]...⊕a[n])
=s[l]⊕s[r+1]
应该没有毛病吧...
注意不管正着找还是反着找都要先放一个数——0进去。为什么?
正着放时则表示从第1位到第i位,反着放时则表示从第i位到第n位。
这个应该不难懂?
从上文可以知道正着找:区间[l,r]的异或值=s[l−1]⊕s[r]
那么当l==1时,即为s[0]⊕s[r]。s[0]就是0啦。
我觉得解释得海星???我的语文水平也就这样了。
代码
#include
#include
#include
using namespace std;
int n,len=0,ans=0,biao=1;
struct node{int son[5];}tr[12400010];
int a[400010],big[400010],l[400010],r[400010];
void bt(int aa,int q)
{
int now=0,x,d=0,k[40];
int kkk=biao,aans=0;
for(int i=1;i<=31;i++)
{
if(aa>=kkk) x=1,aa-=kkk;
else x=0;
if(tr[now].son[x]==0)
{
len++,tr[now].son[x]=len,now=len;
tr[now].son[0]=tr[now].son[1]=0;
}
else now=tr[now].son[x];
d++; k[d]=x;
kkk/=2;
}
now=0; kkk=biao;
for(int i=1;i<=31;i++)
{
if(tr[now].son[1-k[i]]!=0)
{
aans+=kkk;
now=tr[now].son[1-k[i]];
}
else now=tr[now].son[k[i]];
kkk/=2;
}
big[q]=aans;
}
int main()
{
scanf("%d",&n); l[0]=0; r[n+1]=0;
for(int i=1;i<=30;i++) biao*=2;
int s=0; bt(0,0);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s^=a[i]; bt(s,i);
l[i]=max(l[i-1],big[i]);//前半段:以i结尾
}
for(int i=0;i<=len;i++)
{
memset(tr[i].son,0,sizeof(tr[i].son));
}
len=0; s=0; bt(0,0);
for(int i=n;i>=1;i--)
{
s^=a[i]; bt(s,i);
r[i]=max(r[i+1],big[i]);//后半段:以i开头
}
for(int i=1;i<=n;i++)
{
ans=max(ans,l[i]+r[i+1]);
}
printf("%d",ans);
return 0;
}
题目大意
给出若干数字串,判断是否有一个数字串是另一个串的前缀。
数字串只包含0,1,记每个数字串长度为l,则1<=l<=10。每组数据至少有2个数字串,至多有8个数字串。
题目分析
跟#10049. 「一本通 2.3 例 1」Phone List一模一样...不过换了个皮囊。
代码
#include
#include
#include
using namespace std;
int len,d=0;
char aa[20];
bool kkk=false,a;
struct node{int end,s[3];}tr[1010];
void bt(char ss[],int s)
{
int now=0; bool q=false;
for(int i=1;i<=s;i++)
{
if(tr[now].s[ss[i]-'0'])
{
now=tr[now].s[ss[i]-'0'];
if(tr[now].end) a=true;
}
else
{
len++; tr[now].s[ss[i]-'0']=len;
now=len; tr[now].end=0; q=true;
}
}
tr[now].end++;
if(!q) a=true;
}
int main()
{
//freopen("a.out","w",stdout);
while(1)
{
a=false; len=0; d++;
memset(tr[0].s,0,sizeof(tr[0].s));
while(1)
{
if(scanf("%s",aa+1)==EOF) return 0;
if((strlen(aa+1)==1)&&aa[1]=='9') break;
bt(aa,strlen(aa+1));
}
if(a) printf("Set %d is not immediately decodable\n",d);
else printf("Set %d is immediately decodable\n",d);
for(int i=1;i<=len;i++)
{
for(int j=0;j<=1;j++) tr[i].s[j]=0;
tr[i].end=0;
}
}
return 0;
}
#10053. 「一本通 2.3 练习 2」L 语言
题目
题目大意
我们称一段文章T在某个字典D下是可以被理解的,是指如果文章T可以被分成若干部分,且每一个部分都是字典D中的单词。
给定一个字典D,你需要判断若干段文章在字典D下是否能够被理解。
并给出其在字典D下能够被理解的最长前缀的位置。
题目分析
一眼AC自动机?听我的不要慌。不用AC自动机我我我能做!
好吧,字典树+暴力(最喜欢暴力了...滑稽)
代码好懂?不管不管反正我觉得好懂。
一些解释放代码里了。
代码
#include
#include
#include
using namespace std;
char a[1050010];
bool v[1050010];
int n,m,len=0,ans;
struct node{int son[28];bool end;}tr[210];
void bt(char s[],int l)//建树
{
int now=0;
for(int i=1;i<=l;i++)
{
int x=s[i]-'a';
if(tr[now].son[x]) now=tr[now].son[x];
else
{
len++; tr[now].son[x]=len;
now=len; tr[now].end=false;
}
}
tr[now].end=true;
}
void find(int c,int l)
{
if(v[c]) return ;
//如果当前位置被访问过了,那么不会有比之前访问过的更优的情况了
//please仔细想想为什么!
int now=0; v[c]=true;
ans=max(ans,c-1);//必须要是完整的单词
for(int i=c;i<=l;i++)
{
int x=a[i]-'a';
if(tr[now].son[x])
{
now=tr[now].son[x];
if(tr[now].end) find(i+1,l);
//可以作为一个新的开头来
}
else break;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",a+1);
bt(a,strlen(a+1));
}
for(int i=1;i<=m;i++)
{
memset(v,false,sizeof(v));
ans=0; scanf("%s",a+1);
find(1,strlen(a+1));
printf("%d\n",ans);
}
return 0;
}
#10054. 「一本通 2.3 练习 3」Secret Message 秘密信息
#2012. 「SCOI2016」背单词
我太菜了还不会,留坑。猛然惊觉我好多坑没填。
#10056. 「一本通 2.3 练习 5」The XOR-longest Path
题目
题目大意
给定一棵n个点的带权树,求树上最长的异或和路径。
对于100%的数据,1<=n<=10^5,1<=u,v,<=n,0<=w<2^31。
题目分析
甚妙。我努力解释一下。
根据每个结点到根结点的异或值建一棵字典树。
在字典树上努力向两边跑。道理同理于#10050. 「一本通 2.3 例 2」The XOR Largest Pair
从而得出最大答案。
同理#10050. 「一本通 2.3 例 2」The XOR Largest Pair,你可以每插入一个数就跑一遍,建完树再跑也行。
那么问题来了,为什么可以这样做???
无非两种情况
1、两个数在根节点的同侧。
举个例子:1为根节点,2为1的儿子,3为2的儿子(一条链)。那么我们事先处理好了1到2的异或值和1到3的异或值。(敲黑板重点来了!!!)我们可以直接异或1到2的异或值和1到3的异或值然后得到2到3的异或值。因为异或有x⊕x=0的性质。所以任意两个在根节点同侧的点可以得到异或值。
2、两个数在根节点的异侧。
再举个例子:1为根节点,2为1的第一个儿子,3为1的第二个儿子(他好像并没有保证是二叉树)。那么2到3的异或值显然能表示为1到2的异或值 异或 1到3的异或值。
所以一切好像都没什么毛病。
注意!每个数到根节点的异或值也可以作为答案。
那么,以上。
代码
#include
#include
#include
using namespace std;
long long biao=1,ans=0;
int n,len=0,tot=0,a[100010];
struct nod1{int y,gg;long long c;}b[200010];
struct nod2{int son[20];}tr[3100010];
void ins(int x,int y,long long c)
{
len++; b[len].y=y; b[len].c=c;
b[len].gg=a[x]; a[x]=len;
}
void bt(long long x)
{
int now=0,ee[40]; long long kk=biao;
for(int i=32;i>=1;i--)
{
int xx;
if(x>=kk) xx=1,x-=kk;
else xx=0;
if(tr[now].son[xx]) now=tr[now].son[xx];
else tot++,tr[now].son[xx]=tot,now=tot;
kk/=2; ee[i]=xx;
}
now=0,kk=biao; long long ss=0;
for(int i=32;i>=1;i--)
{
if(tr[now].son[1-ee[i]])
{
now=tr[now].son[1-ee[i]];
ss+=kk;
}
else now=tr[now].son[ee[i]];
kk/=2;
}
ans=max(ans,ss);
}
void dfs(int x,long long s,int fa)
{
for(int i=a[x];i;i=b[i].gg)
{
int y=b[i].y;
if(y==fa) continue;
long long aaaaa=s^b[i].c;//每个数到根节点的异或值
bt(aaaaa); dfs(y,aaaaa,x);
ans=max(ans,aaaaa);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=31;i++) biao*=2;
for(int i=1;i