2021年大题

省A第一场

F、砝码称重

你有一个天平和若干砝码,砝码重量依次为W1,W2,W3…Wn,请你计算一共可以称出多少不同的重量?
【样例输入】
3
1 4 6
【样例输出】
10
【样例说明】
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11
1 = 1;
2 = 6 − 4 (2=6−4(天平一边放 6,另一边放 4);
3 = 4 − 1;
4 = 4;
5 = 6 − 1;​
6 = 6;
7 = 1 + 6;
9 = 4 + 6 − 1;
10 = 4 + 6;
11 = 1 + 4 + 6。
评测用例规模与约定
对于 50%的评测用例,1 ≤ N ≤ 15。
对于所有评测用例,1 ≤ N ≤ 100,N​个砝码总重不超过 100000。
我只会递归骗一半分

#define maxn 101
using namespace std;
int w[maxn],visit[maxn];
map<int,int> m;
void dfs(int cnt,int x){
	if (x>0) m[x]=1;
	else return; //倒着来负了,前面更不可能
	if (cnt==-1) return;//递归结束 
	m[w[cnt]]=1;//自己放进去 
	int L[4]={x+w[cnt],x-w[cnt],x,w[cnt]};//累加,和-自己,不要自己,从自己开始 
	for (int i=0;i<4;i++){
		if (visit[cnt-1]==0){
			visit[cnt-1]=1;
			dfs(cnt-1,L[i]);
			visit[cnt-1]=0;
		}
	}
}
int main(){
	int n;
	cin>>n;
	for (int i=0;i<n;i++){
		cin>>w[i];
		visit[i]=0;
	}
	sort(w,w+n);//存储并升序排列
	visit[n-1]=1;
	dfs(n-2,w[n-1]);//有最后一个数
	dfs(n-2,0);//没有最后一个数 
	cout<<m.size();
}

dp[i][j]表示第i个状态,重量为j是否存在。则dp[1][a[1]=1],i状态下的a[i]重量存在(dp[i][a[i]]=1),i-1状态所有合法的j+a[i],abs(j-a[i])也存在。

int dp[101][100001]={0},a[101];
int main(){
	int n,sum=0,ans=0;
	cin>>n;
	for (int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	dp[1][a[1]]=1;//dp[i][a[j]] i表示第几个状态,a[j]表示重量
	for (int i=2;i<=n;i++){//n个状态
		int x=a[i];//把值赋给x便于编写
		for (int j=1;j<=sum;j++){//把上一个状态同重量时 赋给当前状态
			dp[i][j]=dp[i-1][j];
		}
		dp[i][x]=1;//只放一个a[j] 
		for (int j=1;j<=sum;j++){//开始加减传入数
			if (dp[i-1][j]==1){//有这个重量 
				dp[i][j+x]=1;//加
				dp[i][abs(j-x)]=1;//减
			}
		}
	}
	for (int j = 1; j <= sum; j++){
		if (dp[n][j] == 1) ans++;
	}
	cout<<ans;
	return 0;
}

G、异或序列

在这里插入图片描述
【输入】
4
1 1
1 0
2 2 1
7 992438 1006399 781139 985280 4729 872779 563580
【输出】
1
0
1
1
555博弈论推规则也太难了吧TAT:由异或运算性质可知,二进制下每一位可以单独看,每一位之间互不影响。两个数比大小,就是比谁的高位1更高。因此统计每个位置所有数和的1的数量,从高位往低位看比较大小。
1、假设在某位1的数量为偶数,无论怎么异或Alice和Bob的数在这位都相同,要到低一位继续检查;
2、假设在某位,1只有一个,那么先手的Alice肯定会把1加给自己,则Alice获胜,不需要继续往低位看了;
3、假设在某位,1的数量为大于1的奇数,那么拿到最后1个1的人获胜,此时考虑0的个数:
a) 若0的个数为偶数(总数为奇数),Alice先手拿1,之后无论Bob拿什么,Alice跟着拿同样的数字(即Bob取1,Alice也取1;Bob取0,Alice也取0)。由于Alice拿走第一个1后,1和0都剩下偶数个,所以Alice一定可以拿到最后的1,即Alice必胜。
b) 若0的个数为奇数(总数为偶数),Alice先手拿1则Bob拿0,Alice先手拿0则Bob拿1,第一轮结束后,留给先手的状态为1的个数和0的个数都为偶数,由3.1可知该状态为必败态(Bob只需模仿Alice即可保证自己拿到最后的1),即Bob必胜。
4、若所有位置检查完了,1的数量都是偶数,则是平局。

void solve(){
  int n,cnt[25]={0},maxp=0;
  cin>>n;
  for(int i=1;i<=n;i++){
    int p=0,t;
    cin>>t;
    while(t){
      cnt[p++]+=(t&1);
      maxp=max(p,maxp);
      t>>=1;
    }
  }
  int result=0;
  for(int i=maxp-1;i>=0;i--){
    if(cnt[i]&1){//cnt[i]是奇数个1 
      result=(n&1||cnt[i]==1)?1:-1;
      break;
    }//否则偶数个1继续检查下一个位置 
  }
  printf("%d\n",result);
}
int main(){
  int T;
  cin>>T;
  while(T){
    solve();
    T--;
  }
  return 0;
}

H、左孩子右兄弟

对于一棵多叉树,我们可以通过 “左孩子右兄弟” 表示法,将其转化成一棵二叉树。如果我们认为每个结点的子结点是无序的,那么得到的二叉树可能不唯一。换句话说,每个结点可以选任意子结点作为左孩子,并按任意顺序连接右兄弟。给定一棵包含 N​​ 个结点的多叉树,结点从 1​​ 至 N​ 编号,其中 1 号结点是根,每个结点的父结点的编号比自己的编号小。请你计算其通过 “左孩子右兄弟” 表示法转化成的二叉树,高度最高是多少。
注:只有根结点这一个结点的树高度为 0​。
【输入描述】
输入的第一行包含一个整数 N​​​。 以下 N −1行,每行包含一个整数,依次表示 2​ 至 N号结点的父结点编号。
【输出描述】输出一个整数表示答案。
【示例 1】输入
5
1
1
1
2
输出
4
【评测规模与约定】:30%数据1<=N<=20,所有N<=100000
思路:就是存下父亲节点、自己是谁、有几个子节点、是多叉树的第几层。根据层数和孩子个数排序,按右兄弟排列,同层最后一个节点左孩子展开。能得前面那6分。。。
【未过测试点】
1000 1 1 1 3 5 2 2 2 9 5 1 12 5 9 14 3 14 13 19 2 14 7 1 1 11 5 11 14 27 30 23 30 33 17 22 34 18 34 29 40 39 17 8 27 6 14 13 19 19 19 32 39 12 34 16 32 5 54 6 16 25 13 43 48 40 1 2 36 68 25 39 15 1 45 39 15 22 42 58 54 1 58 57 33 38 61 82 37 71 38 60 5 40 87 19 9 4 49 88 82 23 59 88 70 9 4 78 89 8 30 29 37 34 37 99 104 33 100 72 33 25 70 41 117 52 12 110 24 67 18 77 75 60 110 4 85 53 12 91 8 4 72 77 58 123 97 102 113 132 3 117 67 36 151 43 143 154 25 150 24 71 4 89 108 146 51 107 100 5 24 53 111 140 117 32 87 157 17 77 120 91 69 55 101 158 29 109 122 158 155 143 9 24 124 87 131 4 192 66 122 38 37 13 144 141 190 121 190 71 24 94 157 208 75 193 108 73 179 123 113 48 176 132 187 131 116 111 74 202 189 16 224 173 205 1 106 174 109 230 169 3 44 141 42 112 201 63 116 51 161 104 199 151 83 90 71 11 197 6 159 141 25 152 88 26 99 37 86 69 65 44 18 217 65 230 83 160 68 212 255 235 26 154 94 23 69 225 124 191 234 93 153 280 245 294 10 282 164 227 37 190 272 32 216 106 300 23 159 23 48 299 309 256 135 150 283 315 158 199 109 61 105 26 234 69 217 285 136 238 185 136 290 261 31 201 22 83 336 78 208 212 176 33 252 2 60 275 120 260 295 136 131 120 224 99 25 214 200 81 220 119 324 94 133 167 248 154 353 70 353 39 180 129 349 311 367 74 152 239 298 84 371 225 266 356 305 275 316 203 326 305 201 23 293 20 98 353 174 222 289 180 117 64 377 133 372 390 295 273 392 399 310 240 138 405 323 389 9 407 102 369 240 107 371 264 25 230 387 341 29 138 399 167 323 407 84 285 307 436 157 286 339 427 411 414 417 328 102 326 401 39 54 90 30 55 453 47 442 14 296 332 55 275 409 411 91 365 395 113 113 12 33 323 208 403 453 52 361 67 264 182 372 327 41 332 404 252 398 387 259 172 461 63 295 420 290 270 75 423 361 236 264 90 159 138 306 108 217 72 141 320 407 222 350 156 403 359 130 78 235 466 389 85 237 229 371 53 474 279 198 46 498 432 43 37 133 526 323 6 6 517 33 437 454 459 456 210 464 144 10 464 139 188 476 475 23 318 9 306 322 472 412 210 122 536 91 406 285 79 248 462 179 15 95 426 164 157 91 385 55 249 502 534 520 118 444 326 548 322 281 326 410 487 152 187 129 260 274 158 579 128 593 518 84 484 88 60 146 183 128 399 537 160 59 134 389 1 411 154 399 514 563 150 331 130 387 579 418 37 15 237 232 495 456 440 230 416 559 109 472 115 455 390 201 488 118 6 430 442 641 60 145 628 254 324 309 567 3 621 585 36 308 618 637 587 442 616 365 449 429 74 224 517 68 321 17 635 10 482 342 64 680 382 551 356 412 303 255 607 560 270 434 3 168 351 560 471 232 264 49 302 688 368 55 253 362 538 453 683 101 593 231 241 343 542 695 498 323 571 489 525 526 187 107 51 149 598 288 520 585 337 315 725 721 682 721 727 592 338 287 519 145 371 426 487 563 217 668 348 445 124 269 459 113 227 568 318 531 702 514 650 290 221 529 6 648 369 35 20 109 747 636 742 462 525 426 756 55 737 37 506 581 467 20 782 49 573 425 34 536 153 626 793 633 720 215 109 592 26 743 244 654 350 674 42 803 414 732 706 198 232 547 730 610 217 202 33 698 682 150 200 279 616 416 102 623 420 634 121 117 618 16 110 138 424 694 212 562 809 154 347 117 44 475 182 455 808 362 182 697 671 269 138 604 814 489 99 354 430 159 572 543 466 695 419 845 264 40 186 24 788 745 781 437 810 240 123 97 663 313 715 348 588 230 620 301 90 882 639 372 463 213 616 618 848 102 866 594 509 554 36 735 96 97 855 553 478 126 598 773 241 513 743 347 633 256 532 874 632 353 140 604 710 405 830 848 30 878 384 254 680 763 50 203 919 363 888 649 354 455 218 899 611 374 661 521 787 563 807 5 832 830 260 100 768 857 847 827 336 925 256 255 51 58 401 427 849 673 211 171 118 525 316 47 376 922 584 728 379 940 974 731 289 950 67 81 626 481 225 902 858 341 769 719 134 740 462 183 144
【输出】64

#define maxn 100001
using namespace std;
struct node{int f,num,dep,son;};//父亲,节点几,深度及子节点个数 
node dp[maxn];
bool cmp(node a,node b){
	if (a.dep==b.dep) return (a.son<b.son);
	return (a.dep<b.dep);
}
void father(int x){ //祖辈节点个数增加 
	if (x!=-1){//没到头 
		dp[x].son+=1;
		father(dp[x].f);
	}
}
int main(){
	int in,n,maxi=0;
	cin>>n;
	dp[0]={-1,0,0,0};//根高度为0
	for (int i=1;i<n;i++){
		cin>>in;
		dp[i]={in-1,i,dp[in-1].dep+1,0};//初始化 
		father(in-1);//该父亲节点多了一个子节点 
		maxi = max(maxi,dp[in-1].dep+1);//最深 
	} 
	sort(dp,dp+n,cmp);
	
	int res=0,cnt=1,f,fa=0,nextf;//计数,从第1层遍历 
	while (1){
		for (int i=1;i<n;i++){//是该父亲的孩子 
			if (dp[i].dep==cnt&&dp[i].f==fa){
				nextf=dp[i].num;
				res+=1;
			}
		}
		fa = nextf; //下一个父亲 
		cnt+=1;//下一层
		f=1;
		for (int i=1;i<n;i++){
			if (dp[i].num==fa && dp[i].son==0) f=0;
		}	
		if (f==0) break;//没孩子 
	}
	cout<<res; 
	return 0;
}

用vector数组存储孩子,递归搜索最大深度(孩子)+高度(兄弟)

#include <bits/stdc++.h>
using namespace std;
int n,f;
vector<int> v[100005];
int height(int node){
	int maxi=0;//初始化
	for(int i=0;i<v[node].size();i++)
		maxi=max(maxi,height(v[node][i]));//递归,找max(深度+高度)
	return maxi+v[node].size();//高度+深度
}
int main(){
	cin>>n;
	for(int i=2;i<=n;i++){
		cin>>f;
		v[f].push_back(i);
	}
	cout<<height(1);//递归
	return 0;
}

I、括号序列

给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。例如,对于括号序列 (((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果:()()()、()(())、(())()、(()())和 ((()))。
【输入描述】
输入一行包含一个字符串 s,表示给定的括号序列,序列中只有左括号和右括号。
【输出描述】
输出一个整数表示答案,答案可能很大,请输出答案除以 1000000007 (即 10^9 + 7)的余数。
【输入】
((()
【输出】
5
评测用例规模与约定
对于 40% 的评测用例,∣s∣≤200。
对于所有评测用例,1≤∣s∣≤5000。

J、分果果

在这里插入图片描述
【输入】
5 2
6 1 2 7 9
【输出】
0
【输入】
5 5
6 1 2 7 9
【输出】
2
对于 30%​​ 的评测用例,1≤n≤10,1≤m≤10,1≤wi≤10;
对于 60%​ 的评测用例,1≤n≤30,1≤m≤20,1≤wi≤30;
对于所有评测用例,1≤n≤100,1≤m≤50,1≤wi ≤100。在评测数据中wi随机生成在某个区间均匀分布。

第二场

F、小平方

小蓝发现,对于一个正整数 n 和一个小于 n 的正整数 v,将 v 平方后对 n取余可能小于 n 的一半,也可能大于等于 n 的一半。请问,在 1 到 n − 1 中,有多少个数平方后除以 n 的余数小于 n 的一半。例如,当 n = 4 时,1, 2, 3 的平方除以 4 的余数都小于 4 的一半。又如,当 n = 5 时,1, 4 的平方除以 5 的余数都是 1,小于 5 的一半。而2, 3 的平方除以 5 的余数都是 4,大于等于 5 的一半。
对于所有评测用例,1 ≤ n ≤ 10000。
思路:暴力遍历按条件计数即可。

int main(){
	int n,cnt=0;
	cin>>n;
	for (int i=1;i<n;i++){
		if ((i*i)%n<(n+1)/2) cnt+=1;
	} 
	cout<<cnt;
	return 0;
}

G、完全平方数

一个整数 a 是一个完全平方数,是指它是某一个整数的平方,即存在一个整数 b,使得 a = b*b。给定一个正整数 n,请找到最小的正整数 x,使得它们的乘积是一个完全平方数。
【Sample Input 1】
12
【Sample Output 1】
3
【Sample Input2】
15
【Sample Output2】
15
Limitation
对于 30% 的评测用例,1≤n≤1000,答案不超过 1000。
对于 60% 的评测用例,1 ≤ n ≤ 10^8
对于所有评测用例,1 ≤ n ≤ 10^12
思路:遍历n的所有因子,当其有奇数个该因子时将结果乘它,即拆分成多个平方之积

typedef long long ll;
map<ll,ll> m; //记录因子及其个数 
int main(){
	ll n,t,res=1;
	cin>>n;
	t=n;
	for (int i=2;i<sqrt(n);i++){
		while (t%i==0){
			if (m.find(i)==m.end()) m[i]=1;
			else m[i]+=1;
			t/=i;
		}
		if (m.find(i)!=m.end()&&m[i]%2==1) res*=i;//该因子只有奇数个 
		while (t%(n/i)==0){
			if (m.find(n/i)==m.end()) m[n/i]=1;
			else m[n/i]+=1;
			t/=(n/i);
		}
		if (m.find(n/i)!=m.end()&&m[n/i]%2==1) res*=(n/i);//该因子只有奇数个 
	}
	if (t>1) cout<<t;//质数 
	else cout<<res;
	return 0;
}

H、负载均衡

有 n 台计算机,第 i 台计算机的运算能力为 vi。
有一系列的任务被指派到各个计算机上,第 i 个任务在 ai 时刻分配,指定计算机编号为 bi ,耗时为 ci 且算力消耗为 di 。如果此任务成功分配,将立刻开始运行,期间持续占用 bi 号计算机 di 的算力,持续 ci 秒。对于每次任务分配,如果计算机剩余的运算能力不足则输出 −1,并取消这次分配,否则输出分配完这个任务后这台计算机的剩余运算能力。
【输入格式】
输入的第一行包含两个整数 n, m,分别表示计算机数目和要分配的任务数。
第二行包含 n 个整数 v1, v2, · · · vn,分别表示每个计算机的运算能力。
接下来 m 行每行 4 个整数 ai, bi, ci, di,意义如上所述。数据保证 ai 严格递
增,即 ai < ai+1。
【输出格式】
输出 m 行,每行包含一个数,对应每次任务分配的结果。
【样例输入】
2 6
5 5
1 1 5 3
2 2 2 6
3 1 2 3
4 1 6 1
5 1 3 3
6 1 3 4
【样例输出】
2
-1
-1
1
-1
0
【样例说明】
时刻 1,第 1 个任务被分配到第 1 台计算机,耗时为 5 ,这个任务时刻 6
会结束,占用计算机 1 的算力 3。
时刻 2,第 2 个任务需要的算力不足,所以分配失败了。
时刻 3,第 1 个计算机仍然正在计算第 1 个任务,剩余算力不足 3,所以
失败。
时刻 4,第 1 个计算机仍然正在计算第 1 个任务,但剩余算力足够,分配
后剩余算力 1。
时刻 5,第 1 个计算机仍然正在计算第 1, 4 个任务,剩余算力不足 4,失
败。
时刻 6,第 1 个计算机仍然正在计算第 4 个任务,剩余算力足够,且恰好
用完。
【评测用例规模与约定】
对于 20% 的评测用例,n, m ≤ 200。
对于 40% 的评测用例,n, m ≤ 2000。
对于所有评测用例,1 ≤ n, m ≤ 200000,1 ≤ ai, ci, di, vi ≤ 10^9,1 ≤ bi ≤ n。
思路:用vector[]存储输入信息,计算机之间独立所以按计算机进行判断,用queue存储算力恢复序列,并对含有该计算机的时刻进行 是否需要恢复体力及能否执行任务的判断,将结果保存在int数组中,最后按照输入顺序输出数组中的值。写了我一个小时啊TAT太菜了debug要好久

#include <vector> 
#include <queue> 
#include <cmath>
#define maxn 200001
struct node{int ai,ci,di,num;};
vector<node> info[maxn];//存储信息 
vector<node> v;
int power[maxn],res[maxn];//原始、剩余算力
int main() {
	int n,m,in,a,b,c,d,t;
	cin>>n>>m;
	for (int i=1;i<=n;i++) cin>>power[i];
	//把信息存储起来 
	for (int i=1;i<=m;i++){
		cin>>a>>b>>c>>d;
		info[b].push_back({a,c,d,i});
	}
	//分计算机看 
	for (int i=1;i<=n;i++){
		t = power[i];
		v = info[i];
		queue<node> q;//记录恢复序列 
		node p;
		for (int j=0;j<v.size();j++){
			if (q.size()){//恢复体力 
				while (q.size()){
					p=q.front();
					if (p.ai>v[j].ai) break;
					t+=p.di;
					q.pop();
				}	
			}
			if (t>=v[j].di){//可以计算 
				t-=v[j].di;
				res[v[j].num]=t;
				q.push({v[j].ai+v[j].ci,0,v[j].di,0});
			}
			else res[v[j].num]=-1;//不能算 
		}
	}
	//结果输出 
	for (int i=1;i<=m;i++) cout<<res[i]<<endl;
    return 0;
}

I、国际象棋

在这里插入图片描述
【输入格式】
输入一行包含三个正整数 N, M, K,分别表示棋盘的行数、列数和马的个数。
【输出格式】
输出一个整数,表示摆放的方案数除以 1000000007 (即 10^9 + 7) 的余数。
【样例输入】
1 2 1
【样例输出】
2
【样例输入】
4 4 3
【样例输出】
276
【样例输入】
3 20 12
【样例输出】
914051446
【评测用例规模与约定】
对于 5% 的评测用例,K = 1;
对于另外 10% 的评测用例,K = 2;
对于另外 10% 的评测用例,N = 1;
对于另外 20% 的评测用例,N, M ≤ 6,K ≤ 5;
对于另外 25% 的评测用例,N ≤ 3,M ≤ 20,K ≤ 12;
对于所有评测用例,1 ≤ N ≤ 6,1 ≤ M ≤ 100,1 ≤ K ≤ 20。

J、完美序列

在这里插入图片描述
在这里插入图片描述
【输入】
5
1
2
3
5
10
【输出】
1
3
21
140
226800
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值