JOI Open 2021

发布时间 2023-09-27 22:03:02作者: Zhou_JK

交配 / Crossing

J\(0\)O\(1\)I\(2\),杂交相当于是 \(c\equiv -(a+b)\pmod 3\)

可以发现最后的状态数不会超过 \(27\) 种,直接暴力线段树维护即可。


#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
const int N=200005;
const int BASE=233;
const int MOD1=1000000007,MOD2=1000000009;
int n,q;
bool f[3][3][3];
pair<int,int> pw[N],fac[4][N];
char sa[N],sb[N],sc[N];
char t[N];
int get_val(char ch)
{
    if(ch=='J') return 0;
    else if(ch=='O') return 1;
    else if(ch=='I') return 2;
    else return -1;
}
map<pair<int,int>,bool>book;
struct Segment_Tree
{
    #define ls i*2
    #define rs i*2+1
    struct Node
    {
        int l,r;
        int tag;
        pair<int,int> val;
    }tree[N*4];
    Node merge(const Node &a,const Node &b)
    {
        Node c;
        c.l=a.l,c.r=b.r;
        c.tag=0;
        c.val=make_pair(((long long)a.val.first*pw[b.r-b.l+1].first+b.val.first)%MOD1,((long long)a.val.second*pw[b.r-b.l+1].second+b.val.second)%MOD2);
        return c;
    }
    void push_up(int i)
    {
        tree[i]=merge(tree[ls],tree[rs]);
        return;
    }
    void add(int i,int v)
    {
        tree[i].tag=v;
        tree[i].val=fac[v][tree[i].r-tree[i].l+1];
        return;
    }
    void push_down(int i)
    {
        if(!tree[i].tag) return;
        add(ls,tree[i].tag);
        add(rs,tree[i].tag);
        tree[i].tag=0;
        return;
    }
    void build(int i,int l,int r)
    {
        tree[i].l=l,tree[i].r=r;
        if(l==r) return add(i,get_val(t[l])+1);
        int mid=(l+r)/2;
        build(ls,l,mid);
        build(rs,mid+1,r);
        push_up(i);
        return;
    }
    void modify(int i,int l,int r,int v)
    {
        if(l<=tree[i].l&&tree[i].r<=r) return add(i,v);
        push_down(i);
        if(l<=tree[ls].r) modify(ls,l,r,v);
        if(r>=tree[rs].l) modify(rs,l,r,v);
        push_up(i);
        return;
    }
    Node query(int i,int l,int r)
    {
        if(l<=tree[i].l&&tree[i].r<=r) return tree[i];
        push_down(i);
        if(r<=tree[ls].r) return query(ls,l,r);
        else if(l>=tree[rs].l) return query(rs,l,r);
        else return merge(query(ls,l,r),query(rs,l,r));
    }
    #undef ls
    #undef rs
}T;
int main()
{
    scanf("%d",&n);
    scanf("%s",sa+1);
    scanf("%s",sb+1);
    scanf("%s",sc+1);
    f[1][0][0]=f[0][1][0]=f[0][0][1]=true;
    while(true)
    {
        bool flag=true;
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                for(int k=0;k<3;k++)
                    if(f[i][j][k])
                        for(int o=0;o<3;o++)
                            for(int p=0;p<3;p++)
                                for(int q=0;q<3;q++)
                                    if(f[o][p][q])
                                    {
                                        int a=(6-i-o)%3,b=(6-j-p)%3,c=(6-k-q)%3;
                                        if(!f[a][b][c])
                                        {
                                            f[a][b][c]=true;
                                            flag=false;
                                        }
                                    }
        if(flag) break;
    }
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            for(int k=0;k<3;k++)
                if(f[i][j][k])
                {
                    int sum1=0,sum2=0;
                    for(int l=1;l<=n;l++)
                    {
                        int v=(get_val(sa[l])*i+get_val(sb[l])*j+get_val(sc[l])*k)%3;
                        v=(v+3)%3;
                        sum1=((long long)sum1*BASE+v+1)%MOD1;
                        sum2=((long long)sum2*BASE+v+1)%MOD2;
                    }
                    book[make_pair(sum1,sum2)]=true;
                }
    pw[0]=make_pair(1,1);
    for(int i=1;i<=n;i++)
        pw[i]=make_pair((long long)pw[i-1].first*BASE%MOD1,(long long)pw[i-1].second*BASE%MOD2);
    for(int v=1;v<=3;v++)
        for(int i=1;i<=n;i++)
            fac[v][i]=make_pair(((long long)fac[v][i-1].first*BASE+v)%MOD1,((long long)fac[v][i-1].second*BASE+v)%MOD2);
    scanf("%d",&q);
    scanf("%s",t+1);
    T.build(1,1,n);
    if(book[T.query(1,1,n).val]) printf("Yes\n");
    else printf("No\n");
    while(q--)
    {
        int l,r;
        char c;
        scanf("%d%d",&l,&r);
        for(c=getchar();c!='J'&&c!='O'&&c!='I';c=getchar());
        T.modify(1,l,r,get_val(c)+1);
        if(book[T.query(1,1,n).val]) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

決算報告 / Financial Report

\(f_i\) 表示以 \(i\) 结尾的答案最大时多少,考虑转移,当 \(a_j< a_i\)\([j+1,i-1]\)\(\ge a_i\) 的数组成的所有连续段长度 \(<D\) 时有 \(f_i=f_j+1\)

考虑优化,将所有数按照 \(a_i\) 排序,当前算出了 \(1\sim a_i-1\)\(f\),可以用并查集找到从 \([i-d,i-1]\) 中最左边能跳到的位置 \(p\),那么 \(f_i=\max\limits_{j=p}^{i-1}f_j+1\)


#include<iostream>
#include<cstdio>
#include<numeric>
#include<algorithm>
using namespace std;
const int N=300005;
int n,d;
int a[N];
int f[N];
struct Segment_Tree
{
    #define ls i*2
    #define rs i*2+1
    struct Node
    {
        int l,r;
        int mx;
    }tree[N<<2];
    void push_up(int i)
    {
        tree[i].mx=max(tree[ls].mx,tree[rs].mx);
        return;
    }
    void build(int i,int l,int r)
    {
        tree[i].l=l,tree[i].r=r;
        if(l==r)
        {
            tree[i].mx=0;
            return;
        }
        int mid=(l+r)/2;
        build(ls,l,mid);
        build(rs,mid+1,r);
        push_up(i);
        return;
    }
    void modify(int i,int u,int v)
    {
        if(tree[i].l==tree[i].r)
        {
            tree[i].mx=v;
            return;
        }
        if(u<=tree[ls].r) modify(ls,u,v);
        else modify(rs,u,v);
        push_up(i);
        return;
    }
    int query(int i,int l,int r)
    {
        if(l>r) return 0;
        if(l<=tree[i].l&&tree[i].r<=r) return tree[i].mx;
        int res=0;
        if(l<=tree[ls].r) res=max(res,query(ls,l,r));
        if(r>=tree[rs].l) res=max(res,query(rs,l,r));
        return res;
    }
    int find_right(int i,int l,int r)
    {
        if(!tree[i].mx) return 0;
        if(tree[i].l==tree[i].r) return tree[i].l;
        int res=0;
        if(r>=tree[rs].l) res=find_right(rs,l,r);
        if(res) return res;
        if(l<=tree[ls].r) return find_right(ls,l,r);
        else return 0;
    }
    int find_left(int i,int l,int r)
    {
        if(!tree[i].mx) return 0;
        if(tree[i].l==tree[i].r) return tree[i].l;
        int res=0;
        if(l<=tree[ls].r) res=find_right(ls,l,r);
        if(res) return res;
        if(r>=tree[rs].l) return find_right(rs,l,r);
        else return 0;
    }
    #undef ls
    #undef rs
}T;
int fa[N];
int getf(int v)
{
    if(v==fa[v]) return fa[v];
    else return fa[v]=getf(fa[v]);
}
void merge(int u,int v)
{
    int f1=getf(u),f2=getf(v);
    if(f1!=f2) fa[f2]=f1;
    return;
}
int main()
{
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    static int id[N];
    iota(id+1,id+n+1,1);
    sort(id+1,id+n+1,[=](const int &x,const int &y){return a[x]<a[y];});
    T.build(1,1,n);
    iota(fa+1,fa+n+1,1);
    for(int i=1,j=1;i<=n;i=j)
    {
        while(j<=n&&a[id[i]]==a[id[j]]) j++;
        for(int k=i;k<j;k++)
        {
            int pre=T.find_right(1,id[k]-d,id[k]-1);
            f[id[k]]=max(T.query(1,pre?getf(pre):max(id[k]-d,1),id[k]-1)+1,1);
        }
        for(int k=i;k<j;k++)
        {
            T.modify(1,id[k],f[id[k]]);
            int pre=T.find_right(1,id[k]-d,id[k]-1),suf=T.find_left(1,id[k]+1,id[k]+d);
            if(pre) merge(pre,id[k]);
            if(suf) merge(id[k],suf);
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,f[i]);
    printf("%d",ans);
    return 0;
}

モンスターゲーム / Monster Game

如果我们把这个比较丢到 sort 里面,最后会得到若干段相邻递减 \(1\) 的总体上升的序列

询问 \((i,i-2)\) 可以判断这段递减的结束了没有。

但是长度 \(< 3\) 的需要特殊处理


#include<iostream>
#include<cstdio>
#include<vector>
#include<functional>
#include<map>
#include<algorithm>
#include"monster.h"
using namespace std;
map<pair<int,int>,bool>book;
bool query(int a,int b)
{
    if(book.count(make_pair(a,b))) return book[make_pair(a,b)];
    else return book[make_pair(a,b)]=Query(a,b);
}
vector<int> Solve(int N)
{
    int n=N;
    vector<int>id(n);
    for(int i=0;i<n;i++)
        id[i]=i;
    function<void(int,int)>merge_sort=[&](int l,int r)
    {
        if(l==r) return;
        int mid=(l+r)/2;
        merge_sort(l,mid);
        merge_sort(mid+1,r);
        vector<int>tmp;
        int p1=l,p2=mid+1;
        while(p1<=mid&&p2<=r)
            if(!query(id[p1],id[p2])) tmp.emplace_back(id[p1]),p1++;
            else tmp.emplace_back(id[p2]),p2++;
        while(p1<=mid)
            tmp.emplace_back(id[p1]),p1++;
        while(p2<=r)
            tmp.emplace_back(id[p2]),p2++;
        for(int i=0;i<(int)tmp.size();i++)
            id[l+i]=tmp[i];
        return;
    };
    merge_sort(0,n-1);
    for(int i=0,j=0;i<n;i=j)
    {
        if(i==0)
        {
            if(query(id[i+2],id[i])) // 012 0321 1032
            {
                if(query(id[i+1],id[i])) //0321 1032
                {
                    for(j=i+3;j<n;j++)
                        if(!query(id[j],id[i])) break;
                    if(j+1<=n) j++;
                    if(query(id[j-1],id[i+1])) reverse(id.begin()+i,id.begin()+i+2),reverse(id.begin()+i+2,id.begin()+j);
                    else reverse(id.begin()+i+1,id.begin()+j);
                    continue;
                }
                else j=i+1;//012
            }
            else //021 102 210 3210
            {
                int cnt=0;
                int k=i+2;
                for(k=i+2;k<n;k++)
                    if(query(id[i],id[k]))
                    {
                        cnt++;
                        if(cnt>=2) break;
                    }
                    else break;
                if(cnt<2) //021 102 210
                {
                    if(i+5<n)
                    {
                        if(!query(id[i+3],id[i+5])&&(!query(id[i+3],id[i])||!query(id[i+3],id[i+1])||!query(id[i+3],id[i+2]))) //210354 021354 102354
                        {
                            if(!query(id[i+3],id[i])) reverse(id.begin()+i,id.begin()+i+3);
                            else if(!query(id[i+3],id[i+1])) reverse(id.begin()+i+1,id.begin()+i+3);
                            else reverse(id.begin()+i,id.begin()+i+2);
                            reverse(id.begin()+i+3,id.begin()+i+6);
                            j=i+6;
                            continue;
                        }
                        else if(!query(id[i+3],id[i+5])&&(!query(id[i+3],id[i])||!query(id[i+3],id[i+1])||!query(id[i+3],id[i+2]))) //210435 021435 102435
                        {
                            if(!query(id[i+3],id[i])) reverse(id.begin()+i,id.begin()+i+3);
                            else if(!query(id[i+3],id[i+1])) reverse(id.begin()+i+1,id.begin()+i+3);
                            else reverse(id.begin()+i,id.begin()+i+2);
                            reverse(id.begin()+i+3,id.begin()+i+6);
                            j=i+6;
                            continue;
                        }
                    }
                    for(j=i+3+2;j<n;j++)
                        if(!query(id[j-2],id[j])) break;
                    if(!query(id[j-1],id[i])) reverse(id.begin()+i,id.begin()+i+3); //210
                    else if(!query(id[j-1],id[i+1])) reverse(id.begin()+i+1,id.begin()+i+3); //021
                    else reverse(id.begin()+i,id.begin()+i+2); //102
                    reverse(id.begin()+i+3,id.begin()+j);
                    continue;
                }
                else //3210
                {
                    for(j=k+1;j<n;j++)
                        if(!query(id[j-2],id[j])) break;
                }
            }
        }
        else
        {
            for(j=i;j<n;j++)
                if(!query(id[j],id[i-1])) break;
            if(j+1<=n) j++;
        }
        reverse(id.begin()+i,id.begin()+j);
    }
    vector<int>s(n);
    for(int i=0;i<n;i++)
        s[id[i]]=i;
    return s;
}