LOJ 一本通提高篇2.3Trie字典樹 例題+練習(坑)

複習時食用,會比較簡略。

原理就不講了,還不會字典樹的先下車吧。


目錄

#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<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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章