複習時食用,會比較簡略。
原理就不講了,還不會字典樹的先下車吧。
目錄
#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
#10054. 「一本通 2.3 練習 3」Secret Message 祕密信息
#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<cstdio>
#include<cstring>
#include<algorithm>
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<l_2≤r_2≤N,x⨁y表示x和y的按位異或。
對於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<i,使得s[j]⊕s[i]最大。
以上引自: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<cstdio>
#include<cstring>
#include<algorithm>
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;
}
#10052. 「一本通 2.3 練習 1」Immediate Decodability
題目大意
給出若干數字串,判斷是否有一個數字串是另一個串的前綴。
數字串只包含0,1,記每個數字串長度爲l,則1<=l<=10。每組數據至少有2個數字串,至多有8個數字串。
題目分析
跟#10049. 「一本通 2.3 例 1」Phone List一模一樣...不過換了個皮囊。
代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
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 語言
這個哪裏是字典樹明明是AC自動機好不好。或者有什麼奇奇怪怪的方法太弱了也不會。
還沒碼,等會吧。
#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<cstdio>
#include<cstring>
#include<algorithm>
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<n;i++)
{
int x,y; long long z;
scanf("%d%d%lld",&x,&y,&z);
ins(x,y,z); ins(y,x,z);
}
dfs(1,0,0);
printf("%lld",ans);
return 0;
}