2020 CSP-J復賽題解


身為一名高中生,卻還是不知廉恥地做了一遍普及組的題目,體驗一把AK的感覺


T1 優秀的拆分

傳送門

T1還是一如既往的水。

根據題意,奇數直接-1,偶數就從一個很大的2的冪開始枚舉,n比這個數大就輸出這個數並且n減去這個數,然后這個數/2。

#include<iostream>
using namespace std;
int n;
int main() {
    cin>>n;
    if(n&1){
        cout<<-1;
        return 0;
    }
    for(int i=(1<<30);i>0;i/=2){
        if(n>=i){
            cout<<i<<" ";
            n-=i;
        }
    }
    return 0;
}
優秀的拆分

T2 直播獲獎

傳送門

今年的T2還是比較難的,用到了兩個大根堆。

建議先看看這兩個題:

洛谷 P1168 中位數

洛谷 P1801 黑匣子

再看這個題就很板子了。

計算出小根堆的大小,然后不斷push和pop操作即可。

#include<iostream>
#include<queue>
using namespace std;
priority_queue<int> q1;
priority_queue<int,vector<int>,greater<int> > q2;
int n,w,a[100005];
int main(){
    cin>>n>>w;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        int k=max(1,i*w/100);
        if(q2.empty()||q2.top()<=a[i]) q2.push(a[i]);
        else q1.push(a[i]);
        while(!q2.empty()&&q2.size()>k){
            q1.push(q2.top());
            q2.pop();
        }
        while(q2.empty()||q2.size()<k){
            q2.push(q1.top());
            q1.pop();
        }
        cout<<q2.top()<<" ";
    }
    return 0;
}
直播獲獎

T3 表達式

傳送門

這個題就比較毒瘤。不但碼量大,而且規律比較不明顯。

后綴表達式用棧處理,不會的可以看看這個板子:P1449 后綴表達式

這個題先O(n)求出沒取反前的答案,然后對於這q個詢問,很顯然不能每個詢問改變一下再求,所以我們只能去尋找規律,做到O(1)出答案。

我們不難發現,對於每一個操作符,不管操作數是什么,答案最終只可能是0或者1,我們就可以記錄下每一個操作符所對應的結果,並且記錄這個結果是有哪兩個操作數得來的,和這個結果所影響的下一步操作的編號是什么。

存在一個結構體里,即下標表示編號,id表示操作符類型,to表示影響的下一個操作的編號,value表示這一步的值,x1、x2表示兩個操作數(!運算只用到x1)。

然后對於每一次修改,類似記憶化搜索,遞歸判斷對答案有沒有影響,把判斷出來的用vis數組標記一下,即記下變換之后對下一個操作的答案沒有影響。

因為我太菜了,所以一開始的string用了非常笨的方法處理。

#include<iostream>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstring> 
#include<cstdio>
using namespace std;
const int maxn=1000005;
struct node{
    int id;
    int to;
    int value;
    int x1,x2;
}c[maxn*2];
int a[maxn],n,q;
stack<int> s;
string ss;
int cnt=1000000;
int vis[maxn*2],ans;
int sss(string ss){
    int n=0;
    for(int i=1;i<ss.length();i++) n+=(pow(10,ss.length()-i-1)*(ss[i]-'0'));
    return n;
}
int ssss(string ss){
    int n=0;
    for(int i=0;i<ss.length();i++) n+=(pow(10,ss.length()-i-1)*(ss[i]-'0'));
    return n;
}
int dfs(int num){
    if(c[num].id==0) return c[num].value=a[num];
    if(c[num].id==1){
        int x=dfs(c[num].x1),y=dfs(c[num].x2);
        if(x==1&&y==1) return c[num].value=1;
        else return c[num].value=0;
    }
    if(c[num].id==2){
        int x=dfs(c[num].x1),y=dfs(c[num].x2);
        if(x==0&&y==0) return c[num].value=0;
        else return c[num].value=1;
    }
    if(c[num].id==3){
        int x=dfs(c[num].x1);
        if(x==0) return c[num].value=1;
        else return c[num].value=0;
    }
}
bool query(int num){
    if(vis[num]==1) return 1;
    if(vis[num]==2) return 0;
    int too=c[num].to;
    int x=c[too].x1,y=c[too].x2;
    if(x==num) x=y;
    if(c[num].value==0){
        if(c[too].id==1&&c[x].value==1){
            query(too)?vis[num]=1:vis[num]=2;
            return vis[num]==1?1:0;
        }
        if(c[too].id==1&&c[x].value!=1) return vis[num]=1;
        if(c[too].id==2&&c[too].value==0){
            query(too)?vis[num]=1:vis[num]=2;
            return vis[num]==1?1:0;
        }
        if(c[too].id==2&&c[too].value==1) return vis[num]=1;
        if(c[too].id==3){
            query(too)?vis[num]=1:vis[num]=2;
            return vis[num]==1?1:0;
        }
    }
    if(c[num].value==1){
        if(c[too].id==1&&c[x].value==1){
            query(too)?vis[num]=1:vis[num]=2;
            return vis[num]==1?1:0;
        }
        if(c[too].id==1&&c[x].value!=1) return vis[num]=1;
        if(c[too].id==2&&c[x].value==0){
            query(too)?vis[num]=1:vis[num]=2;
            return vis[num]==1?1:0;
        }
        if(c[too].id==2&&c[x].value!=0) return vis[num]=1;
        if(c[too].id==3){
            query(too)?vis[num]=1:vis[num]=2;
            return vis[num]==1?1:0;
        }
    }
    if(c[num].value>1){
        if(c[too].id==1) return vis[num]=1;
        if(c[too].id==2&&c[x].value==0){
            query(too)?vis[num]=1:vis[num]=2;
            return vis[num]==1?1:0;
        }
        if(c[too].id==2&&c[x].value!=0) return vis[num]=1;
        if(c[too].id==3){
            query(too)?vis[num]=1:vis[num]=2;
            return vis[num]==1?1:0;
        }
    }
}
int main(){
    while(1){
        cin>>ss;
        if(ss[0]=='x') s.push(sss(ss));
        else{
            if('0'<=ss[0]&&ss[0]<='9'){
                n=ssss(ss);
                break;
            }
        else{
            if(ss[0]=='&'){
                c[++cnt].id=1;
                c[s.top()].to=cnt;
                c[cnt].x1=s.top();
                s.pop();
                c[s.top()].to=cnt;
                c[cnt].x2=s.top();
                s.pop();
                s.push(cnt);
            }else{
                if(ss[0]=='|'){
                    c[++cnt].id=2;
                    c[s.top()].to=cnt;
                    c[cnt].x1=s.top();
                    s.pop();
                    c[s.top()].to=cnt;
                    c[cnt].x2=s.top();
                    s.pop();
                    s.push(cnt);
                }else{
                    if(ss[0]=='!'){
                        c[++cnt].id=3;
                        c[s.top()].to=cnt;
                        c[cnt].x1=s.top();
                        s.pop();
                        s.push(cnt);
                    }
                }
            }
        }
        }
    }
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    ans=dfs(cnt);
    if(n==1&&cnt==1000000){
        cout<<a[1];
        return 0;
    }
    cin>>q;
    vis[cnt]=2;
    for(int i=1;i<=q;i++){
        int qu;
        scanf("%d",&qu);
        if(query(qu)) printf("%d\n",ans);
        else printf("%d\n",!ans);
    }
    return 0;
}
表達式

T4 方格取數

傳送門

這個題就是一個比較偏板子的dp。

用dp[i][j][0/1/2]分別表示 到第i行 第j列這個點 ,這一列從上往下/從下往上/沒有約束 的最大值。

Q:為什么沒有后效性呢?

A:因為不能往左走,所以可以一列一列處理,所以先枚舉列。

然后就是一頓轉移即可。

注意103*103*104=1010,會爆int!要用long long!

我TM提高組因為沒開long longT2炸了,現在又忘了……

十年oi一場空,不開long long見祖宗。

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1005;
int n,m,a[maxn][maxn];
long long dp[maxn][maxn][3];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=1;i<=n;i++){
        dp[i][1][2]=dp[i-1][1][2]+a[i][1];
    }
    for(int j=2;j<=m;j++){
        dp[1][j][0]=dp[1][j-1][2]+a[1][j];
        for(int i=2;i<=n;i++){
            dp[i][j][0]=max(dp[i][j-1][2],dp[i-1][j][0])+a[i][j];
        }
        dp[n][j][1]=dp[n][j-1][2]+a[n][j];
        for(int i=n-1;i>=1;i--){
            dp[i][j][1]=max(dp[i][j-1][2],dp[i+1][j][1])+a[i][j];
        }
        for(int i=1;i<=n;i++){
            dp[i][j][2]=max(dp[i][j][0],dp[i][j][1]);
        }
    }
    cout<<dp[n][m][2];
    return 0;
} 
方格取數

//期中考試炸了嗚嗚嗚


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM