NOIP2023补题

T1
赛时100pts:
操作相当于冒泡排序,那么直接分解出26字母比较即可
时间复杂度 \(O(26n^2)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define ll long long
#define endl '\n'
const int N=3e3+5;
const int M=1e6+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){putchar('-');x=-x;}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
inl void writei(int x){write(x);putchar(' ');}
int n,m,tong[N],ans[N];
struct node{
    int type,cnt;
    friend bool operator<(node a,node b){return a.type<b.type;}
};
vector<node>v[N];
char s[N];
inl bool comp(int a,int b){
    for(int i=0,j=v[b].size()-1;i<v[a].size()&&~j;i++,j--){
        if(v[a][i].type==v[b][j].type&&v[a][i].cnt==v[b][j].cnt)continue;
        return (v[a][i].type^v[b][j].type)?v[a][i].type<v[b][j].type:0;
    }
    return 0;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=26;j++)tong[j]=0;
        for(int j=1;j<=m;j++)cin>>s[j],tong[s[j]-'a'+1]++;
        for(int j=1;j<=26;j++){
            if(tong[j])v[i].push_back((node){j,tong[j]});
        }
        sort(v[i].begin(),v[i].end());
    }
    for(int i=1;i<=n;i++){
        int flag=1;
        for(int j=1;j<=n;j++){
            if(i==j)continue;
            flag&=comp(i,j);
            if(!flag)break;
        }
        ans[i]=flag;
    }
    for(int i=1;i<=n;i++)cout<<ans[i];cout<<endl;
    return 0;
}

T2
赛时60pts代码:
数据点分治。代码依托答辩。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define ll long long
#define endl '\n'
const int N=1e5+5;
const int M=1e6+5;
const int inf=0x7fffffff;
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){putchar('-');x=-x;}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
inl void writei(int x){write(x);putchar(' ');}
int c,t,n,m;

namespace sub1{
    int a[N],b[N],res;
    struct qus{
        char v;
        int x,y;
    }q[N];
    inl void dfs(int k){
        if(k>n){
            int ans=0,flag=1;
            for(int i=1;i<=n;i++)b[i]=a[i],ans+=(!~a[i]);
            for(int i=1;i<=m;i++){
                switch(q[i].v){
                    case 'T':
                        b[q[i].x]=1;
                        break;
                    case 'F':
                        b[q[i].x]=0;
                        break;
                    case 'U':
                        b[q[i].x]=-1;
                        break;
                    case '+':
                        b[q[i].x]=b[q[i].y];
                        break;
                    default:
                        if(~b[q[i].y])b[q[i].x]=b[q[i].y]^1;
                        else b[q[i].x]=b[q[i].y];
                        break;
                }
            }
            for(int i=1;i<=n;i++)flag&=(a[i]==b[i]);
            if(flag)res=min(res,ans);
            return;
        }
        a[k]=1;dfs(k+1);
        a[k]=0;dfs(k+1);
        a[k]=-1;dfs(k+1);
    }
    inl void solve(){
        while(t--){
            n=read();m=read();res=inf;
            for(int i=1;i<=m;i++){
                scanf(" %c",&q[i].v);
                if(q[i].v=='T'||q[i].v=='F'||q[i].v=='U')q[i].x=read();
                else q[i].x=read(),q[i].y=read();
            }
            dfs(1);
            writel(res);
        }
    }
}

namespace sub2{
    char v;
    int x,y,a[N];
    inl void solve(){
        while(t--){
            memset(a,0,sizeof a);
            n=read();m=read();int ans=0;
            for(int i=1;i<=m;i++){
                scanf(" %c",&v);
                switch(v){
                case 'T':
                    x=read();
                    a[x]=1;
                    break;
                case 'F':
                    x=read();
                    a[x]=0;
                    break;
                case 'U':
                    x=read();
                    a[x]=-1;
                    break;
                case '+':
                    x=read();y=read();
                    a[x]=a[y];
                    break;
                default:
                    x=read();y=read();
                    if(~a[y])a[x]=a[y]^1;
                    else a[x]=a[y];
                    break;
                }
            }
            for(int i=1;i<=n;i++)ans+=(!~a[i]);
            writel(ans);
        }
    }
}

namespace sub3{
    char op;
    int x,y,q[N],h,tt,vis[N];
    struct node{
        int pos,flag;
    }f[N];
    vector<int>v[N];
    inl void solve(){
        while(t--){
            n=read();m=read();
            int ans=0;
            for(int i=1;i<=n;i++)v[i].clear();
            for(int i=1;i<=n;i++)f[i]={i,1};
            memset(vis,0,sizeof vis);
            h=0,tt=1;
            for(int i=1;i<=m;i++){
                scanf(" %c",&op);
                switch(op){
                case 'T':
                    x=read();
                    f[x]={0,1};
                    break;
                case 'F':
                    x=read();
                    f[x]={0,0};
                    break;
                case 'U':
                    x=read();
                    f[x]={0,-1};
                    break;
                case '+':
                    x=read();y=read();
                    f[x]=f[y];
                    break;
                default:
                    x=read();y=read();
                    if(!f[y].pos){
                        if(~f[y].flag)f[x]={0,f[y].flag^1};
                        else f[x]={0,f[y].flag};
                    }else
                        f[x]=f[y];f[x].flag*=-1;
                    break;
                }
            }
            for(int i=1;i<=n;i++){
                // cout<<'/'<<f[i].pos<<' '<<f[i].flag<<endl;
                if(f[i].pos){
                    if(i==f[i].pos){
                        if(!~f[i].flag)q[++h]=i,ans++,vis[i]=1;
                    }else v[f[i].pos].push_back(i);
                }else if(!f[i].pos){
                    if(!~f[i].flag)q[++h]=i,ans++,vis[i]=1;
                }
            }
            while(h>=tt){
                int x=q[tt++];
                for(auto i:v[x])if(!vis[i])q[++h]=i,ans++,vis[i]=1;
            }
            writel(ans);
        }
    }
}
signed main(){
    c=read();t=read();
    if(c==1||c==2)sub1::solve();
    if(c==3||c==4)sub2::solve();
    if(c>=5)sub3::solve();
    return 0;
}

正解:
想到了求最终位置没想到扩展域并查集,鉴定为菜
首先可以对每个点求出他的最终答案是那个位置赋值过来的,那么这两个点初值一定相等,连边,可以用扩展域并查集维护联通块
由于可能反向赋值,用扩展域并查集 两层分别代表 \(T\)\(F\)
对于一个联通块 当存在一个点被赋值成 \(U\) 或者存在一个点 \(T\)\(F\) 联通 该联通块就要全赋值为 \(U\)
时间复杂度 \(O(Tn\log n)\)(这破并查集调了好久/fn

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define endl '\n'
#define int ll
#define gc getchar
#define pc putchar
const int N=1e5+5;
const int M=1e7+5;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int n,m,c,t,fa[N<<1],x,y,fx,fy,vis[N<<1];
char v;
struct node{
    int pos,flag;
}f[N];
inl int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
signed main(){
    c=read();t=read();
    while(t--){
        n=read();m=read();int ans=0;
        for(int i=1;i<=(n<<1);i++)fa[i]=i;
        for(int i=1;i<=n;i++)f[i]={i,1};
        memset(vis,0,sizeof vis);
        while(m--){
            scanf(" %c",&v);
            switch(v){
            case 'T':
                x=read();
                f[x]={0,1};
                break;
            case 'F':
                x=read();
                f[x]={0,0};
                break;
            case 'U':
                x=read();
                f[x]={0,-1};
                break;
            case '+':
                x=read();y=read();
                f[x]=f[y];
                break;
            default:
                x=read();y=read();
                if(!f[y].pos){
                    if(~f[y].flag)f[x]={0,f[y].flag^1};
                    else f[x]={0,f[y].flag};
                }else
                    f[x]={f[y].pos,f[y].flag^1};
                break;
            }
        }
        for(int i=1;i<=n;i++){
            if(!f[i].pos)continue;
            fa[find(i)]=find(f[i].pos+(f[i].flag^1)*n);
            fa[find(i+n)]=find(f[i].pos+f[i].flag*n);
        }
        for(int i=1;i<=n;i++){
            if(!f[i].pos){
                vis[find(i)]=vis[find(i+n)]=!~f[i].flag;
                continue;
            }
        }
        for(int i=1;i<=n;i++){
            ans+=vis[find(i)]||find(i)==find(i+n);
        }
        cout<<ans<<endl;
    }
    return 0;
}

T4
赛时8pts:
似乎咋写都能拿到这8分特殊性质,代码没调出来写的很丑就不放了
正解:
为啥我之前写过一道基本一样线段树优化dp的题还是调不出来啊/fn

发现要跑一定是跑连续段,那么设 \(f_i\) 表示第 \(i\) 天不跑的最优答案

显然有转移:\(f_i=max(f_j+g(j+1,i-1)-(i-j-1)\times d)\)

g为 \([j+1,i-1]\)区间包含的线段的价值和 相当于令 \([j+1,i-1]\) 一直跑

直接转移优化是 \(O(n^2)\)\(O(nm)\)

发现最优情况一定是从某个线段开头跑 从某个线段结尾结束

那么 \(i\) 的取值降到 \(O(m)\) 级别 总复杂度 \(O(m^2)\)

转化一下 \(f_i-i\times d=max((f_j-j\times d)+g(j+1,i-1)+d)\)

显然可以预处理 \(i-1\) 位置结尾的所有线段 然后把贡献累加到线段开头以前的点(区间加)

\(max((f_j-j\times d)+g(j+1,i-1)+d)\) 为区间求 \(max\) 显然可以线段树维护

对于区间不选情况 应该直接继承上一个位置的ans 考试忘了这个没调出来 补题又调一下午/fn

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define endl '\n'
#define int ll
#define gc getchar
#define pc putchar
const int N=2e5+5;
const int M=1e7+5;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int c,t,n,m,k,d,l[N],r[N],a[N],lsh[N];
vector<int>v[N];
struct segment_tree{
    int s[N<<2],tag[N<<2];
    #define mid (l+r>>1)
    #define ls k<<1
    #define rs k<<1|1
    inl void clear(int k,int l,int r){
        s[k]=tag[k]=0;
        if(l==r)return;
        clear(ls,l,mid);clear(rs,mid+1,r);
    }
    inl void pushup(int k){s[k]=max(s[ls],s[rs]);}
    inl void adt(int k,int v){s[k]+=v;tag[k]+=v;}
    inl void pushdown(int k){
        if(!tag[k])return;
        adt(ls,tag[k]);adt(rs,tag[k]);
        tag[k]=0;
    }
    inl void modify(int k,int l,int r,int x,int y,int v){
        if(x<=l&&r<=y)return adt(k,v);
        pushdown(k);
        if(x<=mid)modify(ls,l,mid,x,y,v);
        if(y>mid)modify(rs,mid+1,r,x,y,v);
        pushup(k);
    }
    inl int query(int k,int l,int r,int x,int y){
        if(x<=l&&r<=y)return s[k];
        int ans=-inf;pushdown(k);
        if(x<=mid)ans=max(ans,query(ls,l,mid,x,y));
        if(y>mid)ans=max(ans,query(rs,mid+1,r,x,y));
        return ans;
    }
}SGT;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    c=read();t=read();
    while(t--){
        n=read();m=read();k=read();d=read();
        for(int i=1;i<=m;i++){
            int x=read(),y=read();
            l[i]=lsh[(i<<1)-1]=x-y;
            r[i]=lsh[i<<1]=x+1;
            a[i]=read();
        }
        sort(lsh+1,lsh+(m<<1)+1);
        int p=unique(lsh+1,lsh+(m<<1)+1)-lsh-1;
        for(int i=1;i<=m;i++){
            l[i]=lower_bound(lsh+1,lsh+p+1,l[i])-lsh;
            r[i]=lower_bound(lsh+1,lsh+p+1,r[i])-lsh;
            v[r[i]].push_back(i);
        }
        int lim=1,ans=0;
        SGT.clear(1,1,p);
        SGT.modify(1,1,p,1,1,lsh[1]*d);
        for(int i=2;i<=p;i++){
            for(auto j:v[i])SGT.modify(1,1,p,1,l[j],a[j]);
            while(lsh[i]-lsh[lim]-1>k)lim++;
            ans=max(ans,SGT.query(1,1,p,lim,i-1)-(lsh[i]-1)*d);
            SGT.modify(1,1,p,i,i,ans+lsh[i]*d);
        }
        cout<<ans<<endl;
        for(int i=1;i<=m;i++)v[r[i]].clear();
    }
    return 0;
}
posted @ 2023-11-28 14:32  xiang_xiang  阅读(11)  评论(0)    收藏  举报