无聊来水一发。
历年普及中最简单的几场之一,不懂为什么有人说恶心。去年考场 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