2020 CSPJ

发布时间 2023-11-01 17:54:36作者: shirlybabyyy

 【20CSPJ普及组】优秀的拆分

 直接用二进制处理,不断对2取余,除2,如果奇数,那肯定就是不行的

#include<bits/stdc++.h>
using namespace std;
const int maxn=10100;
const int INF=0x3fffffff;
typedef long long LL;
int n;
int a[maxn];
int main(){
	//二进制??
	cin>>n;
	int i=0;
	if(n==0) {
		printf("-1");
		return 0;
	}
	while(n!=0){
		a[++i]=n%2;
		n/=2;
	} 
	if(a[1]==1) printf("-1");
	else {
		for(int j=i;j>=1;j--){
			if(a[j]) {
				LL ans=pow(2,j-1);
				printf("%lld ",ans);
			} 
		}
	}
return 0;
}

  【20CSPJ普及组】直播获奖

 每个人的成绩都不大于600----->桶排序,直接倒着累加,看人数有没有达到当前人数的w

注意细节哟,应该是判断有没有大于max(1,i*w/100)这个数,因为开始的肯定是人数很少的

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3fffffff;
typedef long long LL;

int n,w;
int t[606];

//每个选手的成绩均为不超过 600600 的非负整数!!!桶排序 
int main(){
	scanf("%d %d",&n,&w);
int x;
	for(int i=1;i<=n;i++){
	cin>>x;
	int summ=0;
	t[x]++;
	for(int j=600;j>=0;j--){
		summ+=t[j];
		if(summ>=max(1,i*w/100)){
			printf("%d ",j);
			break;
		}
	}	
	} 
return 0;
}

  【20CSPJ普及组】表达式

这道题好难。。。。又是后缀表达式,看到题目肯定要反应过来,有的值改变了对结果也没有影响

题目里有个信息是“每个变量在表达式中出现恰好一次”,而每个询问只改变一个变量的值,这对原答案来说就产生两个可能:变或不变。这听起来是一句废话,其实蕴含的意思是:
有些变量对整个表达式其决定作用,其改变则原答案也改变;有些变量对整个表达式根本没用,其变不变原答案都不变。
1 & x = x ,0 | x= x ,这两个公式里的 x 就起了决定性作用,而 0 & x = 0 ,1 | x= 1 的 x 就是一个废物。
那我们就给树上每个结点建一个废物标记,对& 来说,如果一棵子树是 0,那另外一棵子树内所有叶结点都应该打上废物标记,对|同理。
先计算出原表示答案ans,这样我们在查询的时候,没被标记的就说明它往上到根节点都不存在一种让它变成废物的运算,所以答案是!ans,如果有标记则答案依旧为ans。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<cstdlib>
#include<stack>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 1000005;
//好难啊。。。建立表达式树,然后标记(逆转、无用标记)
/*
题目里有个信息是“每个变量在表达式中出现恰好一次”,而每个询问只改变一个变量的值,这对原答案来说就产生两个可能:变或不变。这听起来是一句废话,其实蕴含的意思是:
有些变量对整个表达式其决定作用,其改变则原答案也改变;有些变量对整个表达式根本没用,其变不变原答案都不变。
1 & x = x ,0 | x= x ,这两个公式里的 x 就起了决定性作用,而 0 & x = 0 ,1 | x= 1 的 x 就是一个废物。
那我们就给树上每个结点建一个废物标记,对& 来说,如果一棵子树是 0,那另外一棵子树内所有叶结点都应该打上废物标记,对|同理。
先计算出原表示答案ans,这样我们在查询的时候,没被标记的就说明它往上到根节点都不存在一种让它变成废物的运算,所以答案是!ans,如果有标记则答案依旧为ans。
*/ 
string s;  //字符串
int a[N];   //每个值0/1 
int son[N][2], ck;
int flag[N], c[N];//两个标记
int n, q;
int dfs(int u, int g) {
    a[u] ^= g;
    if (u <= n) {
        return a[u];
    }
    int x = dfs(son[u][0], g ^ flag[son[u][0]]);
    int y = dfs(son[u][1], g ^ flag[son[u][1]]);
    if (a[u] == 2) {
        if (x == 0) c[son[u][1]] = 1; //废物标记
        if (y == 0) c[son[u][0]] = 1;
        return x & y;
    } else {
        if (x == 1) c[son[u][1]] = 1;
        if (y == 1) c[son[u][0]] = 1;
        return x | y;
    }
}
void dfs2(int u) {
    if (u <= n) return;
    c[son[u][0]] |= c[u];
    c[son[u][1]] |= c[u];//合并废物标记
    dfs2(son[u][0]);
    dfs2(son[u][1]); //因为如果上头有废物标记,那就往下传,下面的都是改变了对结
}
int main() {
    getline(cin,s);
    scanf("%d", &n);
    ck = n;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    stack<int> b; //这个存放的是编号
    for (int i = 0; s[i]; i += 2) {  //注意是+=2 
        if (s[i] == 'x') {
            int x = 0;
            i++;
            while (s[i] != ' ') {
                x = x * 10 + s[i] - '0';
                i++;
            }
            i--;//因为是i+=2 
            b.push(x);
        } else if (s[i] == '&') {
            int x = b.top();
            b.pop();
            int y = b.top();
            b.pop();
            b.push(++ck);
            a[ck] = 2;
            son[ck][0] = x;
            son[ck][1] = y;
        } else if (s[i] == '|') {
            int x = b.top();
            b.pop();
            int y = b.top();
            b.pop();
            b.push(++ck);
            a[ck] = 3;
            son[ck][0] = x;
            son[ck][1] = y;
        } else if(s[i] == '!'){
            flag[b.top()] ^= 1;
        }
    }
    int ans = dfs(ck, flag[ck]);
    dfs2(ck);
    scanf("%d", &q);
    while (q--) {
        int x;
        scanf("%d", &x);
        printf("%d\n", c[x] ? ans : !ans);
    }
    return 0;
}

  【20CSPJ普及组】方格取数

 注意这里的方向有三种,而且不能走经过的地方,所以上下下上这样走是不行的,用dfs+记忆化搜索,从终点搜索到起点

记忆化搜索:记录每个点从上来,从下来的值,如果上一步是向上,那么只能走到再上一格,或者是左边(上下都可以

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>

using namespace std;
typedef long long LL;
const int maxn=1e3+10;
const LL INF=1e18;

typedef unsigned long long ull;
//记忆化搜索的方法,要判断上一步是向上还是向右走的
//从终点搜到起点(注意)
//如果上一步是向上,那么只能走到再上一格,或者是左边(上下都可以
int n,m;
int w[maxn][maxn];
LL mp[maxn][maxn][2]; //0表示从一个格子上方走到该格子的路径最大和    1表表示从一个格子下方走到该格子的路径最大和
LL mx(LL p,LL q,LL r){
	return p > q ? (p > r ? p : r) : (q > r ? q : r);
}
LL dfs(int x,int y,int from){
	
	if(x<1||x>n||y<1||y>m) return -INF;
	if(mp[x][y][from]!=-INF) return mp[x][y][from];
	if(from==0) mp[x][y][from]=mx(dfs(x+1,y,0),dfs(x,y-1,0),dfs(x,y-1,1))+w[x][y];
	else mp[x][y][from]=mx(dfs(x-1,y,1),dfs(x,y-1,0),dfs(x,y-1,1))+w[x][y];
	return mp[x][y][from];
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&w[i][j]);
			mp[i][j][0]=mp[i][j][1]=-INF;
		}
	}
	mp[1][1][0]=mp[1][1][1]=w[1][1];
	 LL ans=dfs(n,m,1);
	 printf("%lld",ans);
return 0;
}