CSP2023 游记

发现自己越来越智障了。。。

2:40 开考。四个题看了一遍,T1看起来挺水,T2像某种区间dp,T3大模拟,T4很神秘,完全不会。

2:50 顺序开题。看到T1每次只转一个或连着的两个,看起来很有性质的样子,于是开始研究,发现可以根据五列是否相同来判,然后开始分讨。

(实际上是错的,但没完全错。整场看到5列,每列0~9,居然从没想过1e5的暴力枚举。考完发现把分讨变成判定就是正解。)
(upd10.29:实际上还看错题了。。。以为要求正确密码转一次得到所有状态,所以上面写的很奇怪)

30min 后调过小样例,发现大样例挂了。于是开始研究。

3:50 发现自己没看懂大样例。发现时间过去1h,赶紧开T2。

看到数据范围2e6,发现可能是线性dp。觉得自己想不出来,而且要是再挂可能就保龄了,开始想 \(n^2\) 做法。

4:10 大概想出来了。开码。

4:40 没过大样例&没调出来。怎么会是呢。15min写了拍和 \(n^3\) 暴力,发现在下面情况假了:

\[abbabba \]

我的做法相当于把前两个a配对,但4567也是答案。彻底假了。好似。
(upd10.29:没完全假。枚举左端点就是 \(O(n)\)做法)

发现5:00了。急急急。T2换成 \(n^3\) 暴力,开始犹豫回去看T1还是写T3。觉得T1大家都切了(事实上确实都切了,只有我没想到暴力),于是润回去看T1继续分讨

5:30 发现不对。按照我的做法大样例和答案匹配不上,问志愿者也知道题没错,开始怀疑分讨正确性。(还是没想到暴力枚举)随便乱搞,寄希望于CCF的数据强度了。

这时还没说延不延时,发现可能只剩下1h,T3T4都没写,T1T2加起来也就65(和最后的分一样)。赶紧看T3。

。。。这T3大模拟是给人类写的?开码A性质15pts部分分。

大概15min码完,懒得写拍(实际上没有暴力正解也没法写拍),自己造了几组小样例,感觉没啥问题了(问题很大,15pts挂了)。

开T4。发现每棵树每天长的高度是一次函数的形式,可能用类似李超线段树的东西维护,对于c=0每棵树每天长的高度确定,天数也知道,但神秘贪心很可能假,但没办法链和菊花图不会求天数(感觉像是二分,但还是不会),写了神秘贪心。(然而并没有骗到分)

然后就剩不到20min,查freopen和文件夹,然后就over了

出考场问各位巨佬神秘T1,都说模拟就行,贺老师轻松给出大样例解释,发现自己小丑了。(在车上突然反应过来巨佬们说的模拟是暴力枚举不是分讨,更小丑了

预计得分:30+35+15+0=80
洛谷/小图灵/xyd得分:30+35+0+0=65
实际得分:30+35+0+0=65(不得不说测的真准,我也挂的很稳定)

总结:

好似。打的比任何一场模拟都炸。

反思一下考场上的问题:

  • 小数据范围不想暴力枚举。。。

  • 题没读懂。以为要求只转一次得到所有状态。(被自己蠢哭了

  • 时间分配不均。感觉一半时间都浪费在T1的分讨上导致T3T4没时间写了

  • 考试的时候可能有点着急,感觉思维不够灵活,开局想偏一整场没想过别的方法,死在T1上了

  • 看不懂大样例不要用眼盯,写个暴力试一下(然后就会发现这是正解

  • 写之前先想好,确定是对的在写,T2的假做法卡了我半个多小时。。。

  • 代码能力太差。T3大模拟全挂了。。。

这场确实很难形容。。。希望自己能记住这场的教训,以后别犯这种弱智错误了/fn

赛后补题:

T1

\(10^5\) 枚举所有可能答案 再枚举n个状态 考虑该答案是否正确 分讨

相同位数数量 \(sam=5\)\(sam\le 2\)时 一定不满足(要求 \(n\) 个状态都不是答案 \(sam\le 2\)怎么转都无法实现)

\(sam=4\) 一定满足(不同的一位直接转)

剩下 \(sam=3\) 的情况:

考虑不同的两个位置 \(pos1\ pos2\) 若两个位置不相邻 一定不满足(要求转两个需要相邻)

否则考虑能否转相同幅度达到当前状态 注意可能有进位情况 +-10即可

时间复杂度 \(O(10^5n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writei(int x){write(x);putchar(' ');}
inl void writel(int x){write(x);putchar('\n');}
int n,a[11][11],t[11],pos1,pos2,ans;
signed main(){
    n=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=5;j++)
            a[i][j]=read();
    for(t[1]=0;t[1]<=9;t[1]++){
        for(t[2]=0;t[2]<=9;t[2]++){
            for(t[3]=0;t[3]<=9;t[3]++){
                for(t[4]=0;t[4]<=9;t[4]++){
                    for(t[5]=0;t[5]<=9;t[5]++){
                        int flag=1;
                        for(int i=1;i<=n;i++){
                            int sam=0;
                            for(int pos=1;pos<=5;pos++){
                                sam+=(t[pos]==a[i][pos]);
                                if(t[pos]^a[i][pos]){
                                    pos2=pos1;
                                    pos1=pos;
                                }
                            }
                            if(sam==5||sam<=2){flag=0;break;}
                            if(sam==4)continue;
                            if(abs(pos1-pos2)^1){flag=0;break;}
                            if(a[i][pos1]-t[pos1]==a[i][pos2]-t[pos2])continue;
                            if(a[i][pos1]-t[pos1]+10==a[i][pos2]-t[pos2])continue;
                            if(a[i][pos1]-t[pos1]-10==a[i][pos2]-t[pos2])continue;
                            flag=0;break;
                        }
                        ans+=flag;
                    }
                }
            }
        }
    }
    writel(ans);
    return 0;
}

T2

赛时代码: \(O(n^3)\) 区间dp。

\(f[l][r]\) 表示区间 \([l,r]\) 能否合并。

两种情况:中间消完两边消,或者分两段消。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline
const int N=8005;
const int M=1e5+5;
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
int n,f[N][N],ans;
char c[N];
stack<int>st;
signed main(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    n=read();
    scanf("%s",c+1);
    for(int i=1;i<=n;i++)f[i][i-1]=1;
    for(int len=2;len<=n;len+=2){
        for(int l=1;l+len-1<=n;l++){
            int r=l+len-1;
            if(c[l]==c[r]&&f[l+1][r-1]){f[l][r]=1,ans++;continue;}
            for(int k=l+1;k<r;k+=2){
                if(f[l][k]&&f[k+1][r]){f[l][r]=1,ans++;break;}
            }
        }
    }
    writel(ans);
    return 0;
}

\(O(n^2)\) 做法:

枚举答案左端点,每次看当前元素是否等于栈顶元素,是则弹出,累加答案,否则push进去(类似括号序列)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline
const int N=8005;
const int M=1e5+5;
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
int n,ans;
char c[N];
stack<int>st;
signed main(){
    // freopen("game.in","r",stdin);
    // freopen("game.out","w",stdout);
    n=read();
    scanf("%s",c+1);
    for(int i=1;i<=n;i++){
        stack<char>st;
        for(int j=i;j<=n;j++){
            if(!st.empty()&&c[j]==st.top())
                st.pop(),ans+=st.empty();
            else st.push(c[j]);
        }
    }
    writel(ans);
    return 0;
}

\(90pts\) 做法:

我们发现 当栈的状态和之前某位置时相等 那么说明这区间能全消

可以 hash+map 实现 理论复杂度 \(O(n\log n)\)

但不知道为啥 有两个点T了 有巨佬知道可以帮忙看一下/bx

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline
#define int __int128
const int N=2e6+5;
const int M=1e5+5;
const int base=131;
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
int n,ans;
char c[N];
stack<char>st;
map<int,int>mp;
inl int hash1(stack<char>st){
    int ans=0;
    while(!st.empty()){
        int x=st.top()-'a'+1;st.pop();
        ans=ans*base+x;
    }
    return ans;
}
signed main(){
    freopen("1.in","r",stdin);
    // freopen("game.out","w",stdout);
    n=read();
    scanf("%s",c+1);
    mp[0]=1;
    for(int i=1;i<=n;i++){
        if(!st.empty()&&c[i]==st.top())
            st.pop();
        else st.push(c[i]);
        ans+=mp[hash1(st)];
        mp[hash1(st)]++;
    }
    writel(ans);
    return 0;
}

正解:设 \(f_i\) 表示以 \(i\) 为结尾的合法序列数量 \(ans=\sum f_i\)

考虑转移 发现只要求出以 \(i\) 结尾最短的合法序列

\(lst_i\) 为以 \(i\) 结尾最短的合法序列的开头位置 起始 \(j\) 等于 \(i-1\) 然后往前暴力跳 \(j=lst_j-1\) 直到 \(c_i=c_j\)

( \(j\)\(lst_j\) 是合法序列 那么 \(f_lst-1\) 为下一段开头)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline
#define int ll
const int N=2e6+5;
const int M=1e5+5;
const int base=131;
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
int n,ans,lst[N],f[N];
char c[N];
signed main(){
    n=read();
    scanf("%s",c+1);
    for(int i=1;i<=n;i++){
        int j=i-1;
        if(c[j]==c[i]){f[i]=f[j-1]+1;lst[i]=j;continue;}
        while(lst[j]){
            j=lst[j]-1;
            if(c[j]==c[i]){f[i]=f[j-1]+1;lst[i]=j;break;}
        }
    }
    for(int i=1;i<=n;i++)ans+=f[i];
    writel(ans);
    return 0;
}

T3

赛时 \(15pts\) 做法:\(map\) 存元素对应的地址 每个地址开个 \(string\) 存元素

挂分原因:二操作没输出 \(k\) /fn

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int M=2e5+5;
#define ll long long 
#define inl inline
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
int n,k,op,p;
string vis[805];
map<string,int>mp;
string a,b;
signed main(){
    // freopen("struct.in","r",stdin);
    // freopen("struct.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++){
        op=read();
        if(op==2){
            cin>>a>>b;
            if(a=="byte"){
                writel(k);// 这里/fn
                mp[b]=k;
                vis[k++]=b;
            }
            if(a=="short"){
                while(k%2)k++;
                writel(k);
                mp[b]=k;
                for(int i=1;i<=2;i++)vis[k++]=b;
            }
            if(a=="int"){
                while(k%4)k++;
                writel(k);
                mp[b]=k;
                for(int i=1;i<=4;i++)vis[k++]=b;
            }
            if(a=="long"){
                while(k%8)k++;
                writel(k);
                mp[b]=k;
                for(int i=1;i<=8;i++)vis[k++]=b;
            }
        }
        if(op==3){
            cin>>a;
            writel(mp[a]);
        }
        if(op==4){
            p=read();
            if(vis[p].empty())puts("ERR");
            else cout<<vis[p]<<endl;
        }
    }
    return 0;
}
posted @ 2023-10-22 12:49  xiang_xiang  阅读(58)  评论(0编辑  收藏  举报