2018.8.21 NOIP模拟

开锁匠 unlock.cpp 
【描述】  经济危机席卷全球,L国也收到冲击,大量人员失业。  然而,作为 L 国的风云人物,X 找到了自己的新工作。从下周开始,X 将成为一个 酒店的助理锁匠,当然,他得先向部门领导展示他的开锁能力。  领导给了 X 一串钥匙,这串钥匙串在一个大圆环上,每把钥匙有一个编号 (1..N)。然后蒙上 X 的眼睛并把他带到一个圆形的大房间中。在这个房间中有 N 个上锁 的门,用 1..N 表示,这串 N 把钥匙每一把正好打开一扇门(钥匙编号和门编号一致就可以 打开)。  X 的工作就是打开每扇门。他因为蒙着眼睛,不过可以沿着房间的墙壁移动,不能 改变方向,直到他摸着一扇门,然后他会尝试用第一把钥匙(最左边)来打开门,如果钥 匙不能打开门,他会将钥匙移到另外一侧(最右边),重复这样直到找到正确的钥匙,当 他把所有门打开就结束任务。不过 X不知道的是,领导并不是测试 他开锁能力,而是测试 他的耐心,所以领导故意把 X 带到圆形房间,这样 X 每开一扇门后,领导就会在后面悄悄 把门再次锁上,这样以来,X 打开最后一扇门后又回到第一扇门然后一直重复下去。不过 X 是一个勤奋和耐心的人,他一直毫无怨言的做着这件事,不说任何抱怨的话,只是在每 开一扇门他会默默的统计自己已经错误了多少次,不过慢慢时间太久他的计算能力不足, 需要你来帮助他计算错误的次数。  

任务:给定数字 k,回答当 X打开第 k扇门时,一共错误了多少次?。 【输入】 第一行是 2个整数 N,K 接下来 N 行,每行包含一个整数 Vi,表示钥匙串从第一把(左侧)到最后一把,第 i 把钥 匙的编号

【输出】 一个整数,回答第 k次打开一扇门,已经错误的次数

【输入样例】

4 6

4

2

1

3

【输出样例】 13

【样例解释】

打开第 1扇门的尝试(1号门):4 2 1 3,错误 2次,打开后钥匙排列:1 3 4 2

打开第 2扇门的尝试(2号门):1 3 4 2,错误 3次,打开后钥匙排列:2 1 3 4

打开第 3扇门的尝试(3号门):2 1 3 4,错误 2次,打开后钥匙排列:3 4 2 1

打开第 4扇门的尝试(4号门):3 4 2 1,错误 1次,打开后钥匙排列:4 2 1 3

打开第 5扇门的尝试(1号门):4 2 1 3,错误 2次,打开后钥匙排列:1 3 4 2

打开第 6扇门的尝试(2号门):1 3 4 2,错误 3次,打开后钥匙排列:2 1 3 4

总错误 13 次

【子任务】 40%数据:1<=N,K<=1000 60%数据:1<=K<=50000

100%数据:1<=N<=100000,1<=Vi<=N,1<=K<=10^9 

每n个门循环一次,但是注意第一号门不能计入循环中。例如:3 4 1 2 -> 1 2 3 4-> 2 3 4 1 -> 3 4 1 2-> 4 1 2 3 ->1 2 3 4......

记录一下第i号钥匙到第i+1号钥匙的次数即可。

#include<bits/stdc++.h> 
#define ll long long
#define MAXN 100002
using namespace std;
int N, K, first_key;
int key[MAXN];
ll miss[MAXN];
int main() {
    scanf("%d%d",&N,&K);
    for (int i=0;i<N;++i) {
        int x; scanf("%d",&x);
        key[--x]=i;
        if(i==0) first_key=x;
    }
    ll sol=(ll)abs(key[0]-key[first_key]);
    if (key[first_key]>key[0]) sol = N-sol;
    for (int i=1;i<=N;++i) {
        int curr=key[i-1],prev=key[(i-2+N)%N];
        int wrong=abs(curr-prev);
        if (prev>curr) wrong=N-wrong;
        miss[i]=miss[i-1]+(ll)wrong;
    }
    sol+=miss[N]*(K/N);
    sol+=miss[K%N]-miss[1];    
    printf("%lld\n",sol);
    return 0;

}

位运算 xorand.cpp 
【描述】  有 q次操作,每次操作是以下两种:

1、 加入一个数到集合中

2、 查询,查询当前数字与集合中的数字的最大异或值,最大 and值,最大 or值

【输入】 第一行 1个正整数 Q表示操作次数 接下来 Q 行,每行 2 个数字,第一个数字是操作序号 OP(1,2),第二个数字是 X 表示操 作的数字

【输出】 输出查询次数行,每行 3 个整数,空格隔开,分别表示最大异或值,最大 and 值,最大 or 值

【输入样例 1】

5

1 2

1 3

2 4

1 5

2 7

【输出样例 1】

7 0 7

5 5 7

【样例解释 1】

询问 4时,已插入 2、3,最大异或值为 4^3=7,最大 and值为 4&3或 4&2=0,最大 or值为 4|3=7

询问 7 时,已插入 2、3、5,最大异或值为 7^2=5,最大 and 值为 7&5=5,最大 or 值为 7|2=7|3=7|5=7

【输入样例 2】 10

1 194570

1 202332

1 802413

2 234800

1 1011194

2 1021030

2 715144

2 720841

1 7684

2 85165

【输出样例 2】

1026909 201744 1032061

879724 984162 1048062

655316 682376 1043962

649621 683464 1048571

926039 85160 1011199

【子任务】  

对于%10的数据 1<=Q<=5000  

对于另%10的数据保证 X<1024

 对于另%40的数据保证 1<=Q<=100000  

对于所有数据保证 1<=Q<=1000000,1<=X<=2^20 保证第一个操作为 1操作。 

对于异或运算,用trie树。树上存储集合中所有数的二进制,用01串表示。当查询x的异或最大值时,从最高位开始查,若x的该位为1,则在对应位置上找0,反之找1.贪心完后得到一个数,该数一定在集合中,且与x的异或值最大。

对于and,只需要考虑x的二进制中为1的位置。建立一个数组le,记录每个数的子集。

例如:11对应1011,那么它可以提供的子集有1011,1010,1001,0011,1000,0010,0001,0000;

求and时,尽可能在x的1的位置上填1,这样能使and值最大。求法如下:记录一个数now表示找到的最大值,从高位向下枚举,1的位置上如果能找到1,那么看now补上这个1后是否在le中,在的话就补上,不在就继续向下枚举。最后now&x得到答案。

对于or,和and类似,只需要考虑x的二进制中为0的位置。代码见下:

#include<bits/stdc++.h>
using namespace std;
int root=1,tot=1,trans[(1<<21)][2],xx,op,le[1<<21];
void Markless(int x){
	le[x]=1;
	for(int j=0;j<20;++j)
	if((x>>j&1)&&(!le[x^(1<<j)]))
		Markless(x^(1<<j));
}
void insert(int x){
	int p=root;
	for(int j=19;~j;--j){
		int c=(x>>j&1? 1:0);
		if(!trans[p][c]) trans[p][c]=++tot;
		p=trans[p][c];
	}
}
int ask(int x){
	int p=root,now=0;
	for(int j=19; ~j; --j){
		int c=(x>>j&1? 0:1);
		if (trans[p][c]){
			p=trans[p][c];
			if(c) now|=1<<j;
		}
		else{
			p=trans[p][c^1];
			if(c^1) now|=1<<j;
		}
	}
	return now;
}
int main(){
	int Q;scanf("%d",&Q);
	for(int i=1;i<=Q;++i){
		scanf("%d%d",&op,&xx);
		if(op==1){
			Markless(xx);
			insert(xx);
		}
		if(op==2){
			printf("%d ",ask(xx)^xx);
			
			int now=0;
			for(int j=19;~j;--j){
				if(xx>>j&1&&le[now|1<<j]) now|=1<<j;
			}
			printf("%d ",now&xx);
			
			now=0;
			for(int j=19;~j;--j){
				if(!(xx>>j&1)&&le[now|1<<j]) now|=1<<j;
			}
			printf("%d\n",now|xx);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/81914529