CSP-J2020 题解

无聊来水一发。

历年普及中最简单的几场之一,不懂为什么有人说恶心。去年考场 T3 没看到一个变量只会用一次结果 350pts 滚出。

#1:优秀的拆分:

题意:

给定正整数 \(n\),求二进制表示中为 \(1\) 的位从高往低输出(最小的位是第 \(0\) 位)。特殊地,如果 \(n\) 是奇数输出 -1.

Data range:\(n \le 10^7\)

分析:

真的需要分析吗。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,ans[100],tot;
int main(){
//	freopen("power.in","r",stdin);
//	freopen("power.out","w",stdout);
	scanf("%d",&n);
	if(n&1){
		printf("-1");
		return 0;
	}
	int q = 1;
	while(n){
		if(n&1){
			ans[++tot] = q;
		}
		q = q<<1;
		n = n>>1;
	}
	for(int i=tot;i>=1;i--){
		printf("%d ",ans[i]);
	}
	return 0;
}

#2:直播获奖:

题意:

给定一个长度为 \(n\) 的非负整数数组 \(a\),对于每个 \(i\),求 \(a_1,a_2,...,a_i\) 里的第 \(q_i\) 个数的值(从大到小)。

Data range:\(n \le 10^5,a_i \le 600\)

分析:

考场眼瞎开始没看到 \(a\) 的值域,浪费 20min 写了对顶堆结果挂了...等写完这题正解已经过去 45 min了。

发现值域限制以后就随便做了,用个桶乱搞。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,p,w,t[610]; 
int main(){
//	freopen("live.in","r",stdin);
//	freopen("live.out","w",stdout); 
	
	scanf("%d%d",&n,&p);
	for(int i=1,s;i<=n;i++){
		int w = (i*p)/100;
		if(w<1)w = 1;
		scanf("%d",&s);
		t[s]++;
		int cnt = 0;
		for(int j=600;j>=0;j--){
			cnt += t[j];
			if(cnt>=w){
				printf("%d ",j);break;
			}
		}
	}
	return 0;
}

#3:表达式:

题意:

给定含有 \(n\) 个变量的长度的逻辑表达式 \(|S|\),每个变量的初始取值为 \(0/1\),运算符只有与,或,非三种,表达式 \(s\) 以后缀表达式形式给出,保证每个变量仅出现一次。有 \(q\) 次询问,每次给定一个 \(x\),询问把第 \(x\) 个变量的值取反以后整个表达式的值。询问之间相互独立,即并不真正修改,只是询问。

Data range:\(|S| \le 10^6,n,q \le 10^5\)

分析:

官方数据是真的水,考场上每次表达式树上暴力修改都 50pts 了。

反正初赛能过都应该掌握表达式树了,建出来以后就能模拟了。

然后发现每次只会修改一个叶子节点(题面里加粗的字考场上没看到),然后发现一个子表达式的值只有被更改和不被更改两种情况。考虑预处理修改一个变量后会不会发生变化,首先预处理出表达式上每个节点为根的子表达式的值,记作 \(val_u\)。然后对于一个点 \(u\),其修改是有意义的必须满足 \(fa_u\) 的修改是有意义的,同时:

  • 如果 \(fa_u\) 的操作是 与 操作,那么只有兄弟的 \(val\)\(1\) 的情况下,修改才是有意义的。

  • 如果 \(fa_u\) 的操作是 或 操作,那么只有兄弟的 \(val\)\(0\) 的情况下,修改才是有意义的。

  • 否则,\(fa_u\) 是 非 操作,修改一定有意义。

自上而下求出每个叶子节点的修改是否有意义,然后 \(O(1)\) 回答询问。

写起来巨大恶心。

#include<bits/stdc++.h>
#define rep(i,a,b) for(ll i=(a);i<=(b);i++)
#define per(i,a,b) for(ll i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define next Cry_For_theMoon
#define il inline
#define pb(x) push_back(x)
#define is(x) insert(x)
#define sit set<int>::iterator
#define mapit map<int,int>::iterator
#define pi pair<int,int>
#define ppi pair<int,pi>
#define pp pair<pi,pi>
#define fr first
#define se second
#define vit vector<int>::iterator
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef double db;
using namespace std;
const int MAXN=1e6+10;
struct Node{
	int id,state,fa,lc,rc,val;
}tree[MAXN];
int st[MAXN],top,val[MAXN];
int n,q,idx[MAXN],a[MAXN],tot,len,root;
string tmp;
char str[MAXN];
void dfs(int u){
	if(tree[u].id>0){
		tree[u].val=a[tree[u].id];
		return;
	}
	if(tree[u].lc){
		dfs(tree[u].lc);
	}
	if(tree[u].rc){
		dfs(tree[u].rc);
	}
	if(tree[u].id==-1){
		tree[u].val=tree[tree[u].lc].val & tree[tree[u].rc].val;
	}else if(tree[u].id==-2){
		tree[u].val=tree[tree[u].lc].val | tree[tree[u].rc].val;
	}else{
		tree[u].val=!tree[tree[u].lc].val;
	}
}
void pre(int u){
	if(u==root){
		tree[u].state=1;
	}else{
		if(tree[tree[u].fa].state){
			if(tree[tree[u].fa].id==-1){
				if(tree[tree[u].fa].lc==u){
					if(tree[tree[tree[u].fa].rc].val==1){
						tree[u].state=1;
					}
				}else{
					if(tree[tree[tree[u].fa].lc].val==1){
						tree[u].state=1;
					}
				}
			}else if(tree[tree[u].fa].id==-2){
				if(tree[tree[u].fa].lc==u){
					if(tree[tree[tree[u].fa].rc].val==0){
						tree[u].state=1;
					}
				}else{
					if(tree[tree[tree[u].fa].lc].val==0){
						tree[u].state=1;
					}
				}
			}else{
				tree[u].state=1;
			}
		}	
	}
	if(tree[u].lc)pre(tree[u].lc);
	if(tree[u].rc)pre(tree[u].rc);
	if(tree[u].id>0){
		val[tree[u].id]=tree[u].state;
	}
}
int main(){
	getline(cin,tmp);
	cin.clear();
	scanf("%d",&n);
	rep(i,1,n){scanf("%d",&a[i]);}
	scanf("%d",&q);
	rep(i,1,q){scanf("%d",&idx[i]);}
	len=tmp.length();
	rep(i,1,len){
		str[i]=tmp[i-1];
	}
	int pos=1;
	while(pos<=len){
		if(str[pos]==‘ ‘){
			pos++;
			continue;
		}
		if(str[pos]==‘x‘){
			pos++;tot++;st[++top]=tot;
			int num=0;
			while(str[pos]>=‘0‘ && str[pos]<=‘9‘){
				num=num*10+str[pos]-‘0‘;
				pos++;
			}
			tree[tot].id=num;
			continue;
		}else{
			if(str[pos]==‘&‘){
				tot++;
				tree[tot].lc=st[top-1];
				tree[tot].rc=st[top];
				tree[tot].id=-1;
				tree[st[top-1]].fa=tot;
				tree[st[top]].fa=tot;
				top-=2;
				st[++top]=tot;
			}else if(str[pos]==‘|‘){
				tot++;
				tree[tot].lc=st[top-1];
				tree[tot].rc=st[top];
				tree[tot].id=-2;
				tree[st[top-1]].fa=tot;
				tree[st[top]].fa=tot;
				top-=2;
				st[++top]=tot;
			}else{
				tot++;
				tree[tot].lc=st[top];
				tree[tot].id=-3;
				tree[st[top]].fa=tot;
				top--;
				st[++top]=tot;
			}
			root=tot;
			pos++;
		}
	}
	dfs(root); 
	pre(root);
	int ret=tree[root].val;
	rep(i,1,q){
		if(val[idx[i]]){
			printf("%d\n",ret^1);
		}else{
			printf("%d\n",ret);
		}
	}
	return 0;
}

#4:方格取数

题意:

给定 \(n\times m\) 方格图,每个格子上有个权值 \(a_{i,j}\)(可能负数)。从左上角 \((1,1)\) 走到右下角 \((n,m)\),一步可以向上,下,右的其中一个方向移动一格。把路径上的所有格子的权值加起来得到路径的权值。求最大的路径的权值。不能重复踏入一个格子两次

Data range:\(|a_{i,j}| \le 10^4,n,m \le 10^3\)

分析:

记忆化确实有点妙,考场上被 T3 恶心(指没读题)后秒出了最难的解法然后写完跑路。

考虑大力 \(dp\),设 \(f(i,j)\) 为走到 \((i,j)\) 可能的最大权值。

然后发现可以一列一列转移,对于 \(f(i,j)\) 考虑其从 \(f(k,j-1)\) 转移而来。不妨设 \(k \le i\) 那么这一列的贡献就是第 \(j-1\) 列上 \([k,i]\) 位置的值,是一个前缀和的形式。

然后发现前缀和里 \(sum_{j,i}\) 是不变的,只需要维护前面 \(f(k,j-1)-sum_{j,k}\) 的最大值就可以了。复杂度 \(O(nm)\)。其实 dp 和记忆化都是利用了一个位置不能走两次的约束。

听说这题和 2019 一样又是抄了 USACO ?

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1010;
int n,m;
long long matrix[MAXN][MAXN],f[MAXN][MAXN],sum[MAXN][MAXN];
int main(){
//	freopen("number.in","r",stdin);
//	freopen("number.out","w",stdout);
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%lld",&matrix[j][i]);
		}
	}
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			sum[i][j] = sum[i][j-1] + matrix[i][j];
		}
	}
	for(int i=0;i<=n;i++){
		f[0][i] = -1e9;
	}
	for(int i=0;i<=m;i++){
		f[i][0] = -1e9;
	}
	f[1][1] = matrix[1][1];
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			if(i==1&&j==1)continue;
			//Down || Right
			f[i][j] = max(f[i][j-1],f[i-1][j]) + matrix[i][j];
		}
		long long maxn = f[i-1][n] + sum[i][n];
		int rlim = (i==1)?n:1;
		for(int j=n-1;j>=rlim;j--){
			//Up
			f[i][j] = max(f[i][j],maxn-sum[i][j-1]);
			maxn = max(maxn,f[i-1][j] + sum[i][j]);
		}
	}
	printf("%lld",f[m][n]);
	return 0;
} 

没有有意思的题/kk

最后被下午的 S 组吊打/cy

CSP-J2020 题解

上一篇:FileReader读取txt文件


下一篇:bootstrap-table参数