寒假集训04day

题目链接

数列之异或

题目描述

1 ⨁ 2 ⨁ ⋯ ⨁ N 1 \bigoplus 2 \bigoplus\cdots\bigoplus N 12N 的值。

A ⨁ B A \bigoplus B AB A A A , B B B 按位异或。

输入格式

1 个整数 N N N

输出格式

1 个整数,表示所求的值。

样例 #1

样例输入 #1

3

样例输出 #1

0

提示

• 对于50% 的数据, 1 ≤ N ≤ 1 0 6 1 \le N \le 10^6 1N106

• 对于100% 的数据, 1 ≤ N ≤ 1 0 18 1 \le N \le 10^{18} 1N1018

代码思路

初看我们只能暴力,但暴力肯定会超时,那么我们就先打表,然后找规律。
对于部分数学问题,暴力无法解决,需要找公式的题,我们可以先打表,看是否能通过打表来找到规律。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,ans;
int main(){
	cin>>n;
	if(n%4==1) cout<<1;
	if(n%4==2) cout<<n+1;
	if(n%4==3) cout<<0;
	if(n%4==0) cout<<n;
	return 0;
}

题目链接

[BJWC2008] 雷涛的小猫

题目背景

原最大整数参见 P1012

题目描述

雷涛同学非常的有爱心,在他的宿舍里,养着一只因为受伤被救助的小猫(当然,这样的行为是违反学生宿舍管理条例的)。在他的照顾下,小猫很快恢复了健康,并且愈发的活泼可爱了。

可是有一天,雷涛下课回到寝室,却发现小猫不见了!经过一番寻找,才发现她正趴在阳台上对窗外的柿子树发呆…

在北京大学的校园里,有许多柿子树,在雷涛所在的宿舍楼前,就有 N N N 棵。并且这 N N N 棵柿子树每棵的高度都是 H H H。冬天的寒冷渐渐笼罩了大地,树上的叶子渐渐掉光了,只剩下一个个黄澄澄的柿子,看着非常喜人。而雷涛的小猫恰好非常的爱吃柿子,看着窗外树上的柿子,她十分眼馋,于是决定利用自己敏捷的跳跃能力跳到树上去吃柿子。

小猫可以从宿舍的阳台上跳到窗外任意一棵柿子树的树顶。之后,她每次都可以在当前位置沿着当前所在的柿子树向下跳 1 1 1 单位距离。当然,小猫的能力远不止如此,她还可以在树之间跳跃。每次她都可以从当前这棵树跳到另外的任意一棵,在这个过程中,她的高度会下降 Delta 单位距离。每个时刻,只要她所在的位置有柿子,她就可以吃掉。整个“吃柿子行动”一直到小猫落到地面上为止。

雷涛调查了所有柿子树上柿子的生长情况。他很想知道,小猫从阳台出发,最多能吃到多少柿子?他知道写一个程序可以很容易的解决这个问题,但是他现在懒于写任何代码。于是,现在你的任务就是帮助雷涛写一个这样的程序。

图为 N = 3 , H = 10 , D e l t a = 2 N=3, H=10, Delta=2 N=3,H=10,Delta=2 的一个例子。小猫按照图示路线进行跳跃,可以吃到最多的 8 8 8 个柿子。

输入格式

第一行有三个以空格分隔的整数,分别代表 N , H , D e l t a N,H,Delta N,H,Delta

接下来的 N N N 行,每行第一个整数为 N i N_i Ni,代表第 i i i 棵树上的柿子数量。

接下来是 N i N_i Ni 个整数,每个整数 T i , j T_{i,j} Ti,j 代表第 i i i 棵柿子树的 T i , j T_{i,j} Ti,j 高度上长有一个柿子。

输出格式

一个整数,即小猫最多吃到的柿子数。

样例 #1

样例输入 #1

3 10 2
3 1 4 10
6 3 5 9 7 8 9
5 4 5 3 6 9

样例输出 #1

8

提示

数据范围及约定

对于全部数据, 1 ≤ N , H ≤ 2000 1 \leq N, H ≤ 2000 1N,H2000 0 ≤ N i ≤ 5000 0 \leq N_i ≤ 5000 0Ni5000 1 ≤ D e l t a ≤ N , 1 ≤ T i , j ≤ H 1 ≤ Delta ≤ N,1 ≤ T_{i,j} ≤ H 1DeltaN,1Ti,jH

输入文件大小不大于 40MB。注意输入输出效率。

来源 Excalibur, 2008。

代码思路

对于求最优解,基本上不是搜索就是dp,搜索肯定会超时,我们考虑dp。
里面的重要参数就是第几颗树和高度,那么我们就可以:

//f[i][j]表示在第i棵树上 高度为j的最多柿子数 

那么就可以很简单的写出状态转移方程了

	//i代表高度 
	for(int i=h;i>=0;i--){
		//j代表第几颗树 
		for(int j=1;j<=n;j++){
			//k代表从第几棵树跳过来 
			for(int k=1;k<=n;k++){
				if(j==k) continue;
				f[j][i]=max( f[j][i],max(f[j][i+1]+sum[j][i],f[k][i+del]+sum[j][i]) );
			}
		}
	}

但我们提交会提示超时,那么我们就开始考虑优化,对于高度和第几颗树,我们是无法优化的,但是从第几颗树跳过来,我们可以发现,无论j是多少,从第几颗树跳过来都是固定的最大值,那么我们可以用一个数组pr来存储i高度的最大值。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e3+10; 
//跳到树顶 每次下降一个单位 跳到其他树 然后下降两个单位 
ll n,h,del,ans;
//f[i][j]表示在第i棵树上 高度为j的最多柿子数 
//sum[i][j]表示在第i棵树 高度为j上有的柿子数 
//pre[i]代表的是高度为i的最大柿子数,这样我们可以减少跳过来的树这一枚举量 
ll f[N][N],sum[N][N],pre[N]; 
int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	cin>>n>>h>>del;
	for(int i=1,t;i<=n;i++){
		cin>>t;
		for(int j=1,tt;j<=t;j++){
			cin>>tt;
			sum[i][tt]++;
		}
	}
	//i代表高度 
	for(int i=h;i>=0;i--){
		//j代表第几颗树 
		for(int j=1;j<=n;j++){
			f[j][i]=sum[j][i]+f[j][i+1];
			f[j][i]=max(f[j][i],pre[i+del]+sum[j][i]);
			pre[i]=max(pre[i],f[j][i]);
		}
	}
	for(int i=1;i<=n;i++){
		ans=max(ans,f[i][0]);
	}
	cout<<ans;
	return 0;
}

这个题是很典型的dp优化问题,对于部分题目,除一些必要的条件以外,我们可以进行优化。
还有一种方法便是换一种方向来思考,重点是dp的状态。

题目链接

外星密码

题目描述

有了防护伞,并不能完全避免 2012 的灾难。地球防卫小队决定去求助外星种族的帮助。经过很长时间的努力,小队终于收到了外星生命的回信。但是外星人发过来的却是一串密码。只有解开密码,才能知道外星人给的准确回复。解开密码的第一道工序就是解压缩密码,外星人对于连续的若干个相同的子串 X \texttt{X} X 会压缩为 [DX] \texttt{[DX]} [DX] 的形式( D D D 是一个整数且 1 ≤ D ≤ 99 1\leq D\leq99 1D99),比如说字符串 CBCBCBCB \texttt{CBCBCBCB} CBCBCBCB 就压缩为 [4CB] \texttt{[4CB]} [4CB] 或者 [2[2CB]] \texttt{[2[2CB]]} [2[2CB]],类似于后面这种压缩之后再压缩的称为二重压缩。如果是 [2[2[2CB]]] \texttt{[2[2[2CB]]]} [2[2[2CB]]] 则是三重的。现在我们给你外星人发送的密码,请你对其进行解压缩。

输入格式

输入一行,一个字符串,表示外星人发送的密码。

输出格式

输出一行,一个字符串,表示解压缩后的结果。

样例 #1

样例输入 #1

AC[3FUN]

样例输出 #1

ACFUNFUNFUN

提示

【数据范围】

对于 50 % 50\% 50% 的数据:解压后的字符串长度在 1000 1000 1000 以内,最多只有三重压缩。

对于 100 % 100\% 100% 的数据:解压后的字符串长度在 20000 20000 20000 以内,最多只有十重压缩。保证只包含数字、大写字母、[]

代码思路

开始我想的便是模拟,毕竟字符串长度一般,我们完全可以进行模拟,但模拟的代码很难写。
那么我们可以考虑递归。
递归函数即实现输入,也实现输出。
将输入也写进递归函数里面,毕竟当我们遇到【时,我们需要输入数字,如果用字符串进行纯模拟的话,很麻烦,但我们可以一步一步输入,这样极大的减少了工程量。

#include<bits/stdc++.h>
using namespace std;
string f()
{
	//s2代表括号里面的数 
    string s1="",s2;
    char ch;
    while(cin>>ch)
    {
        if(ch=='\n')break;
        if(ch=='[')
        {
        	//输入数字 
            int t;
            cin>>t;
            s2=f();
            while(t--)s1+=s2; 
        }
        else if(ch==']') return s1;
        else s1+=ch;
    }
    return s1;
}
int main()
{
    cout<<f();
    return 0;
}

题目链接

硬币的面值

题目描述

小 A 有 n n n 种硬币,现在要买一样不超过 m m m 元的商品,他不想得到找钱(多脏啊),同时又不想带太多的硬币,且硬币可以重复,现在已知这 n n n 种硬币的价值,请问最少需要多少硬币就能组合成所有可能的价格?

输入格式

第一行两个数: n , m n, m n,m

下一行,共 n n n 个数字,表示硬币的面值。

输出格式

一行一个数,表示最少需要多少硬币。如果无解请输出 No answer!!!

样例 #1

样例输入 #1

5 31
1 2 8 4 16

样例输出 #1

5

提示

【数据范围】

只有 9、10 会卡人,放心贪

对于 20 % 20\% 20% 的数据, 1 ≤ n ≤ 10 1 \le n \le 10 1n10 1 ≤ m ≤ 100 1 \le m \le 100 1m100
对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 1000 1 \le n \le 1000 1n1000 1 ≤ m ≤ 10000 1 \le m \le 10000 1m10000
对于 80 % 80\% 80% 的数据, 1 ≤ n ≤ 30000 1 \le n \le 30000 1n30000 1 ≤ m ≤ 2 × 10 9 1 \le m \le 2 \times {10}^9 1m2×109
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 2 × 10 5 1 \le n \le 2 \times {10}^5 1n2×105 1 ≤ m ≤ 2 63 1 \le m \le 2^{63} 1m263

算法思路

题目初看我还以为是dp,毕竟这个题面很像dp的题。
一般来说,如果答案里面有无解的情况,我们一定要优先考虑无解的时候,那么看题目,如果没有1,那么我们肯定无法拼出所有的情况,反之,有1的话,我们肯定可以通过1来拼出所有的情况。
无解的情况已经知道了,那么如何求解呢。
先考虑暴力,我们从1-n一直枚举,那么肯定是可以的,但是我们的m的数据极大,枚举肯定会超时。
换一个思考方向:
1 3 5如何拼出10呢?
1元需要一个1元硬币
2元需要两个1元硬币
3元如何拼呢,肯定是贪心呗,直接拿一个3元硬币。
4元可以拆解为1+3。
那么思路很明确了。
我们写拿小的,小的拿完以后,那么1-he肯定都是能拿到的,对于超过了sum[i]面值的硬币,肯定是优先拿大面值硬币,然后通过小面值硬币来进行拼凑。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+7;
ll n,m,sum[N],ans,he;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    	cin>>sum[i];
	}
	sum[n+1]=m;
	sort(sum+1,sum+n+2);
	if(sum[1]!=1) {
		cout<<"No answer!!!";
		return 0;
	}
	for(int i=1;i<=n;i++){
		while(he<sum[i+1]-1){
			he+=sum[i];
			ans++;
			if(he>=m) {
				cout<<ans;
				return 0;
			}
		}
	}
	cout<<ans+1;
    return 0;
}

题目链接

奶牛分厩

题目描述

农夫约翰有 N ( 1 ≤ N ≤ 5000 ) N(1 \le N \le 5000) N(1N5000) 头奶牛,每头奶牛都有一个唯一的不同于其它奶牛的编号 s i s_i si,所有的奶牛都睡在一个有 K K K 个厩的谷仓中,厩的编号为 0 0 0 K − 1 K-1 K1。每头奶牛都知道自己该睡在哪一个厩中,因为约翰教会了它们做除法, S i   m o d   K S_i \bmod K SimodK 的值就是第 i i i 头奶年所睡的厩的编号。

给出一组奶牛的编号,确定最小的 K K K 使得没有二头或二头以上的奶牛睡在同一厩中。

输入格式

第一行一个正整数 N N N,第 2 2 2 N + 1 N+1 N+1 行每行一个整数表示一头奶牛的编号。

输出格式

一个整数,表示要求的最小的 K K K,对所有的测试数据这样的 K K K 是一定存在的。

样例 #1

样例输入 #1

5 
4 
6 
9 
10 
13

样例输出 #1

8

提示

S i ( 1 ≤ S i ≤ 1000000 ) S_i(1\le S_i \le 1000000) Si(1Si1000000)

算法思路

这题基本上就是哈希的逆向求K。
从正向思考的话,我们基本上只能进行枚举+模拟了,因为K他并不具有单调性,我们不能进行二分,正向思考那基本上没有机会了。
我们可以知道,a,b在mod k 意义下同余,当且仅当 k|(a-b) 即k是(a-b)的一个因子。
那么a和b mod 上两个数的差,一定相等。
这个题中,对于不同的奶牛需要睡在不同的牛棚上,不能用任意两个数的差的倍数。
逆向思考一下,我们先将两个数的差的倍数进行标记,然后我们再从小到大枚举一下,找到的第一个没有被标记的数就是我们需要的值。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e3+7;
const int K = 1e6+7;
ll n,sum[N],f[K];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
    	cin>>sum[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			//对两个数的差值做上标记 
			//如果mod两个数的差值余数一样 不行 
			int t=abs(sum[i]-sum[j]);
			f[t]=1;
		}
	}
	//判断第一个没有被标记的数是什么 找到便输出即可 
	for(int i=n;i<K;i++){
		if(!f[i]){
			int t=1;
			for(int j=i;j<K;j+=i) {
				if(f[j]) {
					t=0;break;
				}
			}
			if(t) {
				cout<<i;
				return 0;
			}
		}
	}
    return 0;
}

题目链接

【CSGRound1】天下第一

题目背景

天下第一的 cbw 以主席的身份在 8102 年统治全宇宙后,开始了自己休闲的生活,并邀请自己的好友每天都来和他做游戏。由于 cbw 想要显出自己平易近人,所以 zhouwc 虽然是一个蒟蒻,也有能和 cbw 玩游戏的机会。

题目描述

游戏是这样的:

给定两个数 x x x y y y,与一个模数 p p p

cbw 拥有数 x x x,zhouwc 拥有数 y y y

第一个回合: x ← ( x + y )   m o d   p x\leftarrow(x+y)\bmod p x(x+y)modp

第二个回合: y ← ( x + y )   m o d   p y\leftarrow(x+y)\bmod p y(x+y)modp

第三个回合: x ← ( x + y )   m o d   p x\leftarrow(x+y)\bmod p x(x+y)modp

第四个回合: y ← ( x + y )   m o d   p y\leftarrow(x+y)\bmod p y(x+y)modp

以此类推…

如果 x x x 先到 0 0 0,则 cbw 胜利。如果 y y y 先到 0 0 0,则 zhouwc 胜利。如果 x , y x,y x,y 都不能到 0 0 0,则为平局。

cbw 为了捍卫自己主席的尊严,想要提前知道游戏的结果,并且可以趁机动点手脚,所以他希望你来告诉他结果。

输入格式

有多组数据。

第一行: T T T p p p 表示一共有 T T T 组数据且模数都为 p p p

以下 T T T 行,每行两个数 x , y x,y x,y

输出格式

T T T

1 1 1 表示 cbw 获胜, 2 2 2 表示 zhouwc 获胜,error表示平局。

样例 #1

样例输入 #1

1 10
1 3

样例输出 #1

error

样例 #2

样例输入 #2

1 10
4 5

样例输出 #2

1

提示

1 ≤ T ≤ 200 1 \leq T \leq 200 1T200

1 ≤ x , y , p ≤ 10000 1 \leq x,y,p \leq 10000 1x,y,p10000

算法思路

基本上就是搜索+模拟的组合。
但这个题的空间和时间都十分的卡人。
空间上我们需要创建short数组。
先不管时间复杂度,直接进行暴力

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e3+7;
int t,k,x,y;
int f[N][N];
void dfs(int a,int b,int c){
	if(f[a][b]==1) {
		cout<<"error"<<endl;
		return;
	}
	f[a][b]=1;
	if(a==0) {
		cout<<1<<endl;
		return;
	}
	if(b==0) {
		cout<<2<<endl;
		return;
	}
	if(c==1) {
		dfs((a+b)%k,b,2);
	} else {
		dfs(a,(a+b)%k,1);
	}
}
int main(){
	cin>>t>>k;
	while(t--){
		cin>>x>>y;
		memset(f,0,sizeof(f));
		dfs(x,y,1);
	}    
    return 0;
}

然后我们会发现超时,为什么呢?
因为我们再搜索枚举的时候,经常会出现已经出现过的值,这时候我们应该直接返回,这就是记忆化搜索。
记忆化搜索的好处:如果搜到以前的便无需再搜,直接返回。对每一个点最多只需找一次。极大的节省了时间。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e4+7;
short t,k,x,y;
//令vis[x][y]为当a为x,b为y且a先手时的胜负情况 
short vis[N][N];
string pr[]={"","1","2","error"};
short dfs(int a,int b){
	if(vis[a][b]==3) {
		return 3;
	}
	if(vis[a][b]!=0) return vis[a][b];
	if(vis[a][b]==0) vis[a][b]=3;
	if(a==0) {
		return vis[a][b]=1;
	}
	if(b==0) {
		return vis[a][b]=2;
	}
	return vis[a][b]=dfs((a+b)%k,((a+b)%k+b)%k);
}
int main(){
	cin>>t>>k;
	while(t--){
		cin>>x>>y;
		dfs(x,y);
		cout<<pr[vis[x][y]]<<endl;
	}    
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值