首页 > 其他分享 >NOIP2023 做题笔记

NOIP2023 做题笔记

时间:2024-10-31 22:00:38浏览次数:4  
标签:return minn int 笔记 mxn maxn NOIP2023 define

NOIP将近,由于我实力太菜,所以只能写写真题提升自己了。

P9868 [NOIP2023] 词典

简单字符串题,注意到可以换无限次,所以直接处理出每个字符串中最小的字符数和最大字符数就行了。


#include<bits/stdc++.h>
#define mxn 3010
using namespace std;
char s[mxn][mxn];
int n,m,cnt[mxn][27];
int minn[mxn],maxn[mxn],flg;
int main(){
    //freopen("dict.in","r",stdin);
    //freopen("a.out","w",stdout); 
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    memset(minn,0x3f,sizeof(minn));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            cin>>s[i][j];
            cnt[i][s[i][j]-'a'+1]++;
            minn[i]=min(minn[i],s[i][j]-'a'+1);
            maxn[i]=max(maxn[i],s[i][j]-'a'+1);
        }
    for(int i=1;i<=n;i++){
        flg=1;
        for(int j=1;j<=n;j++){
            if(i==j)continue;
            if(minn[i]<maxn[j])continue;
            flg=0;break;
        }
        if(flg)cout<<1;
        else cout<<0;
    }
    return 0;
}

P9869 [NOIP2023] 三值逻辑

这题折腾了我一下午+三分之一个晚上,我真菜啊。
考虑用并查集将每个操作模拟。
每个值为 \(unknown\) 当且仅当出现两种情况:
\(1.\ x\) 的祖先是 \(-x\)。
\(2.\ x\) 的祖先为 \(unknwon\)。
这样判断就可以了。
同时,发现查询是会有负数,整体右移就行了。
对于死循环的情况,用 \(vis\) 数组判断就行了。


#include<bits/stdc++.h>
#define mxn 100010
#define U 0
#define T 100001
#define F -100001
using namespace std;
int n,m,_,type,ans;
int f[mxn],vis[mxn<<1];
int fnd(int u){
    if(u==T||u==F||u==U)return u;
    if(vis[-u+n])return U;
    if(vis[u+n])return T;
    if(u>0){
        vis[u+n]=1;
        int ret=fnd(f[u]);
        f[u]=ret;
        vis[u+n]=0;
        return ret;
    }
    else{
        vis[u+n]=1;
        int ret=fnd(-f[-u]);
        vis[u+n]=0;
        return ret;        
    }
    return 0;
}
void solve(){
    ans=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=m;i++){
        char opt;cin>>opt;
        if(opt=='T'){int x;cin>>x;f[x]=T;}
        if(opt=='F'){int x;cin>>x;f[x]=F;}
        if(opt=='U'){int x;cin>>x;f[x]=U;}
        if(opt=='+'){int x,y;cin>>x>>y;f[x]=f[y];}
        if(opt=='-'){int x,y;cin>>x>>y;f[x]=-f[y];}
    }
    for(int i=1;i<=n;i++)
        if(fnd(i)==U)ans++;
    cout<<ans<<'\n';
    return; 
} 
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>type>>_;
    while(_--)solve();
    return 0;
}

P9870 [NOIP2023] 双序列拓展

一开始很容易想到一个 \(35pts\) 的 \(O(qnm)\) 暴力。
根据特殊性质能发现,
我们找到 \(X\) 的最小值与 \(Y\) 的最大值的位置,把那一行和那一列标出来:

(偷题解的图)

发现如果能从左上角走到红线处,就可以从红线处走到右下角。
于是分治计算就行了。


#include<bits/stdc++.h>
#define ll long long
#define mxn 500010
using namespace std;
struct node{
    int minn,maxn;
};
int c,q,a[mxn],b[mxn],xx[mxn],yy[mxn];
node prex[mxn],prey[mxn],sufx[mxn],sufy[mxn];
bool check1(int a,int b,int n,int m){
    if(a==1||b==1)return 1;
    if(xx[prex[a-1].minn]<yy[prey[b-1].minn])return check1(prex[a-1].minn,b,n,m);
    if(xx[prex[a-1].maxn]<yy[prey[b-1].maxn])return check1(a,prey[b-1].maxn,n,m);
    return 0;
}
bool check2(int a,int b,int n,int m){
    if(a==n||b==m)return 1;
    if(xx[sufx[a+1].minn]<yy[sufy[b+1].minn])return check2(sufx[a+1].minn,b,n,m);
    if(xx[sufx[a+1].maxn]<yy[sufy[b+1].maxn])return check2(a,sufy[b+1].maxn,n,m);
    return 0;
}
bool solve(int x[],int y[],int n,int m){
    if(x[1]>=y[1])return 0;
    for(int i=1;i<=n;i++)xx[i]=x[i];
    for(int i=1;i<=m;i++)yy[i]=y[i];
    prex[1]=node{1,1},prey[1]=node{1,1},sufx[n]=node{n,n},sufy[m]=node{m,m};
    for(int i=2;i<=n;i++)
        prex[i]=node{x[prex[i-1].minn]>x[i]?i:prex[i-1].minn,x[prex[i-1].maxn]<x[i]?i:prex[i-1].maxn};
    for(int i=2;i<=m;i++)
        prey[i]=node{y[prey[i-1].minn]>y[i]?i:prey[i-1].minn,y[prey[i-1].maxn]<y[i]?i:prey[i-1].maxn};
    for(int i=n-1;i;i--)
        sufx[i]=node{x[sufx[i+1].minn]>x[i]?i:sufx[i+1].minn,x[sufx[i+1].maxn]<x[i]?i:sufx[i+1].maxn};
    for(int i=m-1;i;i--)
        sufy[i]=node{y[sufy[i+1].minn]>y[i]?i:sufy[i+1].minn,y[sufy[i+1].maxn]<y[i]?i:sufy[i+1].maxn};
    if(x[prex[n].maxn]>=y[prey[m].maxn])return 0;
    if(x[prex[n].minn]>=y[prey[m].minn])return 0;
    return check1(prex[n].minn,prey[m].maxn,n,m)&&check2(prex[n].minn,prey[m].maxn,n,m);
}
int x[mxn],y[mxn],n,m;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>c>>n>>m>>q;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++)cin>>b[i];
    if(solve(a,b,n,m)||solve(b,a,m,n))putchar('1');
    else putchar('0');
    while(q--){
        for(int i=1;i<=n;i++)x[i]=a[i];
        for(int i=1;i<=m;i++)y[i]=b[i];
        int kx,ky;cin>>kx>>ky;
        for(int i=1;i<=kx;i++){
            int p,v;cin>>p>>v;x[p]=v;
        }
        for(int i=1;i<=ky;i++){
            int p,v;cin>>p>>v;y[p]=v;
        }
        if(solve(x,y,n,m)||solve(y,x,m,n))putchar('1');
        else putchar('0');
    }
    return 0;
}   

P9871 [NOIP2023] 天天爱打卡

有点复杂的dp。
朴素的dp是从左到右遍历,枚举左端点。这样做 \(O(Tnmk)\),会炸飞。
然后我们考虑优化,能不能快速维护区间最大值和区间修改?可以用线段树解决。


#include<bits/stdc++.h>
#define ll long long
#define mxn 100010
#define pll pair<ll,ll>
#define pb push_back
#define mp make_pair
using namespace std;
ll n,m,k,d,c,t;
ll b[mxn<<1],len,cnt,dp[mxn<<1];
struct chlg{
    ll x,y,v;
}a[mxn<<1];
vector<pll> p[mxn<<1];
namespace seg{
    ll sum[mxn<<3],lazy[mxn<<3];
    void push_down(ll rot){
        sum[rot<<1]+=lazy[rot],sum[rot<<1|1]+=lazy[rot];
        lazy[rot<<1]+=lazy[rot],lazy[rot<<1|1]+=lazy[rot];
        lazy[rot]=0;
    }
    void push_up(ll rot){
        sum[rot]=max(sum[rot<<1],sum[rot<<1|1]);
    }
    void build(ll rot,int l,int r){
        sum[rot]=lazy[rot]=0;
        if(l==r)return;
        int mid=(l+r)>>1;
        build(rot<<1,l,mid);
        build(rot<<1|1,mid+1,r);
    }
    void add(ll rot,int l,int r,int x,int y,ll k){
        if(l>=x&&r<=y){
            sum[rot]+=k,lazy[rot]+=k;
            return;
        }
        push_down(rot);
        int mid=(l+r)>>1;
        if(x<=mid)add(rot<<1,l,mid,x,y,k);
        if(y>mid)add(rot<<1|1,mid+1,r,x,y,k);
        push_up(rot);
    }
    ll query(ll rot,int l,int r,int x,int y){
        if(l>=x&&r<=y)return sum[rot];
        if(r<x||l>y)return 0;
        push_down(rot);
        int mid=(l+r)>>1;
        return max(query(rot<<1,l,mid,x,y),query(rot<<1|1,mid+1,r,x,y));
    }
}
void init(){
    cnt=0;
    cin>>n>>m>>k>>d;
    for(int i=1;i<=m;i++){
        cin>>a[i].y>>a[i].x>>a[i].v;
        a[i].x=a[i].y-a[i].x+1;
        b[++cnt]=a[i].x,b[++cnt]=a[i].y;
    }
    sort(b+1,b+cnt+1);
    len=unique(b+1,b+cnt+1)-b-1;
    for(int i=0;i<=len;i++)dp[i]=0;
    seg::build(1,1,len);
    return;
}
void solve(){
    init();
    for(int i=1;i<=m;i++){
        a[i].x=lower_bound(b+1,b+len+1,a[i].x)-b;
        a[i].y=lower_bound(b+1,b+len+1,a[i].y)-b;
        p[a[i].y].pb(mp(a[i].x,a[i].v));
    }
    for(int i=1;i<=len;i++){
        for(int j=0;j<p[i].size();j++){
            pll pp=p[i][j];
            seg::add(1,1,len,1,pp.first,pp.second);
        }
        dp[i]=max(dp[i],dp[i-1]);
        int pos1=lower_bound(b+1,b+len+1,b[i]-k+1)-b;
        int pos2=(b[i-1]==b[i]-1)?i-2:i-1;
        ll dpi=dp[pos2>=0?pos2:0];
        dp[i]=max(dp[i],seg::query(1,1,len,pos1,i-1)-b[i]*d-d);
        dp[i]=max(dp[i],seg::query(1,1,len,i,i)+dpi-d);
        seg::add(1,1,len,i,i,dpi+b[i]*d);
    }
    cout<<dp[len]<<'\n';
    for(int i=1;i<=len;i++)p[i].clear();
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>c>>t;
    while(t--)solve();
    return 0;
}

标签:return,minn,int,笔记,mxn,maxn,NOIP2023,define
From: https://www.cnblogs.com/nagato--yuki/p/18515119

相关文章

  • Java进阶学习笔记64——IO流
    IO流:输入输出流,就是读写数据的。IO流的应用场景:怎么去学习IO流?1、先搞清楚IO流的分类、体系?2、再挨个学习每个IO流的作用、用法。IO流的分类:按流的方向分为:按流中数据的最小单位,分为: IO流总体上来看就有四大流:字节输入流: 把磁盘或网络中的数据以一个个......
  • Java进阶学习笔记63——字符集
    常见字符集介绍:美国人:英文字母(大小写)数字、标点符号、特殊字符。标准字符集:ASCII码:标准ASCII字符集:ASCII:美国信息交换标准代码,包括了英文、符号等。标准ASCII使用1个字节存储一个字符,首位是0,总共表示128个字符,对美国人老说完全够用。中国人自己的字符集:GBK(汉字内......
  • 《程序员的修炼者之道》第三次读书笔记
    《程序员的修炼之道——从小工到专家》第三章:基本工具的读书笔记在阅读《程序员的修炼之道——从小工到专家》的第三章时,我深刻感受到了作者们对于编程基本工具的重视。这一章不仅详细介绍了程序员在日常工作中不可或缺的基本工具,还强调了如何有效利用这些工具来提高编程效率和代......
  • 《程序员的修炼之道》第一次读书笔记
    《程序员修炼之道》第一章:注重实效的哲学深度读书笔记在信息技术日新月异的今天,程序员作为推动时代进步的重要力量,其专业素养和实践能力显得尤为重要。《程序员修炼之道》作为一本广受好评的编程指南,为程序员提供了宝贵的经验和深刻的洞见。其中,第一章“注重实效的哲学”更是以......
  • 《程序员的修炼之道》第二次读书笔记
    《程序员的修炼之道》第二章:注重实效的途径——读书笔记在阅读《程序员的修炼之道——从小工到专家》的第二章时,我深刻体会到了作者们在编程实践中所强调的“实效”精神。这一章不仅为我们揭示了编程过程中的许多实用技巧和方法,还强调了程序员在解决实际问题时应保持的灵活性和创......
  • 《程序员修炼之道:从小工到专家》阅读笔记3---石头汤与煮青蛙的启示
    《程序员修炼之道:从小工到专家》中的“石头汤”与“煮青蛙”的故事,给我带来了深刻的启示。“石头汤”的故事告诉我们,在团队协作中,要善于引导他人参与,共同完成项目。当我们在开发过程中需要其他团队配合时,不能只是一味地等待他们的支持,而是要先做出一些成果,让别人看到项目的......
  • LCT 学习笔记
    \(\text{LCT}\)学习笔记可曾久仰\(\text{LCT}\)大名,可曾听闻\(\text{Splay}\)骂名?动态树对于一棵有\(n\)个节点的树,如果每个点都有点权,那么求解\(x,y\)之间的路径上的点权和可以用树链剖分+线段树简单做到。考虑对于一棵\(n\)个节点的动态树,也就是可以删除某一条边......
  • 程序员修炼之路 从小工到专家 第一章读书笔记
    《程序员修炼之道——从小工到专家》的第一章“注重实效的哲学”给我留下了深刻的印象。这一章通过一系列生动的故事和实用的建议,向我们展示了成为一名优秀程序员所需要具备的品质和思维方式。在阅读过程中,我首先被书中提到的“不要害怕暴露弱点”这一观点所吸引。作者认为,......
  • “平板电视”学习笔记
    首先,我们需要导入头文件#include<bits/extc++.h>其次,我们需要导入名字空间usingnamespace__gnu_pbds;然后我们就可以用pbds定义一个集合啦~比如:tree<pair<int,int>,null_type,less<pair<int,int>>,rb_tree_tag,tree_order_statistics_node_update>s;从左至右依次为:Key......
  • 程序员修炼之路 从小工到专家 第二章读书笔记
    在深入阅读了《程序员修炼之路——从小工到专家》的第二章后,我对于程序员的成长路径和专业技能的提升有了更为深刻的理解。这一章主要围绕“构建自己的工具箱”这一主题展开,通过一系列实用的建议和方法,引导我们如何逐步提升自己的编程能力和技术水平。在阅读过程中,我首先被......