JZOJ 3908 吴传之火烧连营

题目大意
题目大意就是对于一个序列,多次询问求在 xor K 的情况下最大值。

原题

Description
【题目背景】
蜀汉章武元年(221年),刘备为报吴夺荆州、关羽被杀之仇,率大军攻吴。吴将陆逊为避其锋,坚守不战,双方成对峙之势。蜀军远征,补给困难,又不能速战速决,加上入夏以后天气炎热,以致锐气渐失,士气低落。刘备为舒缓军士酷热之苦,命蜀军在山林中安营扎寨以避暑热。陆逊看准时机,命士兵每人带一把茅草,到达蜀军营垒时边放火边猛攻。蜀军营寨的木栅和周围的林木为易燃之物,火势迅速在各营漫延。蜀军大乱,被吴军连破四十余营。陆逊火烧连营的成功,决定了夷陵之战蜀败吴胜的结果。
 
【问题描述】
刘备带兵深入吴境,陆逊却避而不出,蜀军只得在山林中安营扎寨。而刘备在扎营时却犯了兵家大忌,将兵营排列成一条直线,远远看去,就像是一条串着珠子的链,美其名曰:链寨。如果吴军将领是一般人,那么这也许不算什么,而陆逊何许人也,他可是江东才子,能力不低于周瑜的一代儒将。他看到刘备这样排阵,心生一计,决定用火攻破阵。然而,火计除了要有风,选定引火点也非常重要,对于刘备的布阵,最佳引火点一定是n个兵营中的一个。而因为风水轮流转,每天的最佳引火点都不一样。我们给每个兵营定下一个固定不变的火攻值Ai,每天定下一个风水值K,对于每天的最佳引火点,显然是所有兵营中火攻值与风水值异或的结果最大的那一个兵营。然而,陆逊是个谨慎的人,他要观察时机,在m天中选定一个最佳的进攻的日期,为此他演算出了这m天每天的风水值,然后他希望你能够告诉他这m天每天最佳引火点的兵营编号。
Input
第一行n,m,代表有n个兵营,m天。
    接下来一行有n个非负整数,代表这n个兵营的火攻值。
    接下来一行有m个非负整数,代表这m天的风水值。
Output
输出共m行,每行输出一个整数,代表第m天最佳引火点的编号。
如果存在多个最佳引火点使得火攻值与风水值的异或值最大,请任意输出一组解即可。
Sample Input

3 2
1 2 3
4 5


Sample Output

 
 

3
2

				【样例解释】<br>对于第1天,由于4&nbsp;xor&nbsp;1=5,&nbsp;4&nbsp;xor&nbsp;2=6,&nbsp;4&nbsp;xor&nbsp;3=7,选择第3个引火点是最佳的。<br>对于第2天,由于5&nbsp;xor&nbsp;1=4,&nbsp;5&nbsp;xor&nbsp;2=7,&nbsp;5&nbsp;xor&nbsp;3=6,选择第2个引火点是最佳的。</pre></div>
			</fieldset></div>
		</div>
		<div class="clearfix"></div>
		<div class="well"><fieldset>
			<legend><h4>Data Constraint</h4></legend>
			<div class="content">对于30%数据,n&lt;=1000,m&lt;=1000<br>对于100%数据,n&lt;=100000,m&lt;=100000,&nbsp;0&lt;=k,ai&lt;=2147483647</div>
		</fieldset></div>
		<div class="clearfix"></div>
		<div style="text-align:center">

思考

异或运算规则

⊕ \oplus 01
001
110

我一开始以为这题要从对异或的拆解入手。
然后写了如下公式:
a ⊕ b = a ∨ b − a ∧ b a\oplus b=a\vee b-a\wedge b ab=abab
a ∨ b = a + b − a ∧ b a\vee b=a+b-a\wedge b ab=a+bab
a ⊕ b = a ⊕ c ⊕ b ⊕ c a\oplus b=a\oplus c \oplus b\oplus c ab=acbc
符号解释: ⊕ ( x o r )    , ∨ ( o r )    , ∧ ( a n d ) \oplus(xor)\; ,\vee(or)\; ,\wedge(and) (xor),(or),(and)

然而并没有什么用。

直到我听到韩冠东大佬(博客在此)在小机房里大喊

“Trie!”

经过了一段思考以后。。
又看了看异或运算。
在二进制下,
K K K的第 j j j位如果是1,那 a i a_i ai的第 j j j位为0的就一定比为1的优(显然)

可以用Trie 在 O ( 32 n ) O(32n) O(32n)的时间跑完贪心了。

Trie(字典树)

英[t’ri:] 美[t’ri:]

通常都是长这样的。
在这里插入图片描述

淡黄色的是一个Bool标记,表示从root到该点存在一条字符串。
如上图,就有字符串"ab",“ac”,“b”,“bb”
本题把所有的 a i a_i ai的二进制看作字符串,放到Trie里就可以了。

#include<cstdio>
#include<cstring>
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
using namespace std;
int A[100010],e[100010];
int to[2][2000010],co=0;
int bz[2000010];
int lgmax=31;
void byTrie(int w)
{
	int i,x=0,v=A[w];
	for(i=lgmax;i>=0;i--)
	{
//		printf("%d ",(v>>i)&1);
		if(to[(v>>i)&1][x]==0) to[(v>>i)&1][x]=++co;
		x=to[(v>>i)&1][x];
	}
//		printf("\n");
	bz[x]=w;
}
int main()
{
	freopen("fire.in","r",stdin);
	freopen("fire.out","w",stdout);
	memset(to,0,sizeof(to));
	int n,m,ans,i,j,x;
	scanf("%d%d",&n,&m);
	int maxA;
	for(i=1;i<=n;i++) scanf("%d",&A[i]),maxA=max(maxA,A[i]);
	for(i=1;i<=n;i++) byTrie(i);
	for(i=1;i<=m;i++) scanf("%d",&e[i]);
	//for(i=0;i<=co;i++) printf("%d %d\n",to[0][i],to[1][i]);
	for(i=1;i<=m;i++)
	{
		x=0;
		for(j=lgmax;j>=0;j--)
		{
			if(to[((e[i]>>j)&1)^1][x]!=0) x=to[((e[i]>>j)&1)^1][x];
			else x=to[((e[i]>>j)&1)][x];
		}
		printf("%d\n",bz[x]);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值