• Codeforces Round #620(Div. 2)


    Codeforces Round #620 (Div. 2)

    题目链接 https://codeforces.com/contest/1313

    A B C题直接跳过。

    D题:

    求最短的上升子序列,我们可以直接假设它是1~n从大到小排列,然后对于每两个大于号之间的小于号再翻转就行。

    求最长的就反过来做一遍就行。

    代码:

        #include<bits/stdc++.h>
        using namespace std;
        int n,a[300050],ans[300050];
        char s[300050];
        void solve(){
            int head=0;
            for(int i=1;i<=n;i++){
                if (s[i]=='>'){
                    for(int j=head+1;j<=i;j++)
                        ans[j]=n-i+j-head;
                    head=i;
                }
            }
        }
        void work(){
            cin>>n>>s+1;
            s[n]='>';
            solve();
            for(int i=1;i<=n;i++)cout<<ans[i]<<" 
    "[i==n];
            for(int i=1;i<n;i++)s[i]^='<'^'>';
            solve();
            for(int i=1;i<=n;i++)cout<<n+1-ans[i]<<" 
    "[i==n];
        }
        int main(){
            int T;
            cin>>T;
            while(T--)work();
        }
    

    E题

    这个题目,如果不加一条边,那么就是如果距离是(L,k=L+2x(x>=1))都可以,因为你可以在一条边来回走。

    增加一条边,你如果走这条边偶数次,相当于没走。走奇数次,奇偶性都一样,所以我们就设置只走一次,那么只要求dist(x,a)+dist(y,b)和dist(x,b)+dist(y,a),令(L2=min(dist(x,a)+dist(y,b),dist(x,b),dist(y,a))+1 ; k=L2+2x)都可以。求举例就有用深度,(dist(x,y)=d[x]+d[y]-2*d[lca(x,y)]), 求lca就用倍增就行了。

    代码:

        #include<bits/stdc++.h>
        using namespace std;
        #define N 200050
        struct Edge{int x,y,s;};
        struct Tree{
            int n;
            int fa[N][20],d[N];
            int tot=0,last[N];
            Edge edges[N<<1];
            void addEdge(int x,int y){
                edges[++tot]=Edge{x,y,last[x]};
                last[x]=tot;
            }
            void readTree(){
                cin>>n;
                for(int i=1;i<=n-1;i++){
                    int x,y;
                    cin>>x>>y;
                    addEdge(x,y);
                    addEdge(y,x);
                }
                dfs(1);
                build();
            }
            void dfs(int u){
                d[u]=d[fa[u][0]]+1;
                for(int i=last[u];i;i=edges[i].s){
                    Edge &e=edges[i];
                    if (fa[u][0]==e.y)continue;
                    fa[e.y][0]=u;
                    dfs(e.y);
                }
            }
            void build(){
                for(int i=1;i<=19;i++)
                    for(int j=1;j<=n;j++)
                        fa[j][i]=fa[fa[j][i-1]][i-1];
            }
            int lca(int x,int y){
                if (d[x]<d[y])swap(x,y);
                for(int i=19;i>=0;i--)
                    if (d[fa[x][i]]>=d[y]) x=fa[x][i];
                if (x==y) return x;
                for(int i=19;i>=0;i--)
                    if (fa[x][i]!=fa[y][i]){
                        x=fa[x][i];
                        y=fa[y][i];
                    }
                return fa[x][0];
            }
            int dist(int x,int y){return d[x]+d[y]-2*d[lca(x,y)];}
            void solve(){
                bool flag=false;
                int x,y,a,b,k;
                cin>>x>>y>>a>>b>>k;
                int t1=dist(a,b);
                int t2=min(dist(a,x)+dist(b,y),dist(a,y)+dist(b,x))+1;
                if (t1<=k && (k-t1)%2==0) flag=true;
                if (t2<=k && (k-t2)%2==0) flag=true;
                string s[]={"NO","YES"};
                cout<<s[flag]<<endl;
            }
        }Tr;
        int main(){
            int T;
            Tr.readTree();
            cin>>T;
            while(T--) Tr.solve();
        }
    

    F题:

    F分成F1和F2。动规题,设(a[i][j])表示第i天j位置的动物数量,设表示第i天在j位置放下超相机,也就是记录

    (dp[i][j])表示第i天在j位置放下超相机,也就是记录$ a[i到i+1][j到j+k-1]$,

    (i)天照相机的位置可能会和第(i-1)照相机有动物重叠。

    所以递推式就是:

    (dp[i][j]=max(dp[i-1][h]-重叠部分)+sum_{x=i}^{i+1}sum_{y=j}^{j+k-1}a[x][y])

    我们会发现,上一个照相机的摆放位置可以分成三种情况:

    • ([1,j-k]): 没有重叠
    • ([j-k+1,j+k-1]):有重叠
    • ([j+k,m]): 没有重叠

    方法一:

    F1, (k)很小,没有重叠的部分只要取最大值就行了,这个很好做,从前扫一遍,从后扫一遍(dp[i-1])就行了,然后重叠部分一个一个判断.

    时间复杂度(O(nmk))

    方法二:

    我们用一个新数组(c[h]=dp[i-1][h]-重合部分).

    对于每个每个位置(j) ,都需要更新(c[i])数组,

    (j 到 j+1)

    • (c[j-k+1 到 j]全部+a[i][j])
    • (c[j+1到j+k]全部-a[i][j+k])
    • 其余的不变

    很明显只要改变两个区间,做两个区间加法就行,于是我们可以用线段树。

    时间复杂度(O(nmlogm))

    方法三:

    就是怎么优化方法二,随之(j)的不断增大,我们发现,中间的重复的部分分成两个部分,左半部分重合部分越来越少,右半部分重合越来越多,并且右半部分重合到最多的时候就开始转入左半部分(因为重合部分越来越少)。

    左半部分范围([j-k+1,j]),其实就是要求所有区间长度为(k)的最大值,这个就和滑动窗口那题很像。用单调队列,保持队列递减,元素为c[h],然后从(j到j+1),用一个变量(t)记录整个区间加了多少,如果后入队的,先把权值减去这个(t)。没有重合的部分还是按照方法一预处理就行。

    右半部分也是一样,再开一个单调队列。

    时间复杂度(O(nm)) (但是我的代码比线段树还慢,后来模仿std重写了一下,发现原来是没有关闭同步,qwq)

    代码一 (O(nmk))

        #include<bits/stdc++.h>
        using namespace std;
        #define N 60
        #define M 30050
        int n,m,k,ans;
        int a[N][M],s[N][M];
        int lmax[M],rmax[M];
        int dp[M],newdp[M];
        int main(){
            #ifndef ONLINE_JUDGE
            freopen("aa.in","r",stdin);
            #endif
            ios::sync_with_stdio(false);
            cin.tie(0);
            cin>>n>>m>>k;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++){
                    cin>>a[i][j];
                    s[i][j]=a[i][j]+s[i][j-1];
                }
            for(int i=1;i<=n;i++){
                for(int j=1;j<=m;j++)
                    lmax[j]=max(lmax[j-1],dp[j]);
                for(int j=m;j>=1;j--)
                    rmax[j]=max(rmax[j+1],dp[j]);
                for(int j=1;j<=m-k+1;j++){
                    int val=s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
                    
                    newdp[j]=max(lmax[max(j-k,0)],rmax[min(j+k,m+1)])+val;
                    
                    for(int h=max(j-k+1,1);h<=min(j+k-1,m);h++){
                        int r=min(j+k-1,h+k-1), l=max(j,h);
                        newdp[j]=max(newdp[j],val+dp[h]-s[i][r]+s[i][l-1]);
                    }
                    //cout<<j<<" "<<newdp[j]<<endl; 
                }
                for(int i=1;i<=m;i++)dp[i]=newdp[i];
                
            }
            for(int i :dp)ans=max(ans,i);
            cout<<ans;
        }
    

    代码二 线段树 (O(nmlog m))

        #include<bits/stdc++.h>
        using namespace std;
        #define M 20050
        #define N 60
        int s[N][M],a[N][M];
        int dp[M],newdp[M];
        struct SegmentTree{
            int L[M<<2],R[M<<2],Max[M<<2],J[M<<2];
            int n;
            SegmentTree (int n){
                this->n=n;
                build(1,1,n);
            }
            void push_up(int x){
                if (L[x]==R[x])Max[x]=0;
                else Max[x]=max(Max[x<<1],Max[x<<1|1]);
                Max[x]+=J[x];
            }
            void push_down(int x){
                J[x<<1]+=J[x];
                J[x<<1|1]+=J[x];
                J[x]=0;
                push_up(x<<1);
                push_up(x<<1|1);
            }
            void build(int x,int l,int r){
                L[x]=l; R[x]=r; Max[x]=J[x]=0;
                if (l==r)return;
                int mid=(l+r)>>1;
                build(x<<1,l,mid);
                build(x<<1|1,mid+1,r);
            }
            void clear(){build(1,1,n);}
            void add(int x,int l,int r,int p){
                if (l>r)return;
                if (l<=L[x] && R[x]<=r){
                    J[x]+=p;
                    push_up(x);
                    return;
                }
                int mid=(L[x]+R[x])>>1;
                push_down(x);
                if (l<=mid) add(x<<1,l,r,p);
                if (mid<r) add(x<<1|1,l,r,p);
                push_up(x);
            }
            int get(){return Max[1];}
        };
        int main(){
        #ifndef ONLINE_JUDGE
            freopen("aa.in","r",stdin);
        #endif
            ios::sync_with_stdio(false);
            cin.tie(0);
            int n,m,k,ans=0;
            cin>>n>>m>>k;
            SegmentTree Seg(m);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++){
                    cin>>a[i][j];
                    s[i][j]=a[i][j]+s[i][j-1];
                }
            for(int i=1;i<=n;i++){
                Seg.clear();
                for(int j=1;j<=k;j++) Seg.add(1,j,j,dp[j]-s[i][k]+s[i][j-1]);
                for(int j=k+1;j<=m;j++) Seg.add(1,j,j,dp[j]);
                for(int j=1;j<=m-k+1;j++){
                    int val=s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
                    newdp[j]=val;
                    newdp[j]=max(newdp[j],Seg.get()+val);
                    Seg.add(1,max(1,j-k+1),j,a[i][j]);
                    Seg.add(1,j+1,min(j+k,m),-a[i][j+k]);
                }
                for(int j=1;j<=m;j++)dp[j]=newdp[j];
            }
            for(int i: dp)ans=max(ans,i);
            cout<<ans;
        }
    

    代码三 单调队列1 第一次写 (O(nm))

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,k,lmax,now1,now2,ans=0;
    int a[55][30050],s[55][30050],dp[55][30050];
    struct Point{int x,val;};
    deque<Point>Q1,Q2,Q3;
    void init(){
        lmax=0;
        now1=0;
        now2=0;
        while(!Q1.empty())Q1.pop_back();
        while(!Q2.empty())Q2.pop_back();
        while(!Q3.empty())Q3.pop_back();
    }
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0);
        #ifndef ONLINE_JUDGE
        freopen("aa.in","r",stdin);
        #endif 
        cin>>n>>m>>k;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                cin>>a[i][j];
                s[i][j]=a[i][j]+s[i][j-1];
            }
        for(int j=1;j<=m-k+1;j++){
            dp[1][j]=s[1][j+k-1]-s[1][j-1]+s[2][j+k-1]-s[2][j-1];
            ans=max(ans,dp[1][j]);
        }
        for(int i=2;i<=n;i++){
            init();
            Q1.push_back(Point{1,dp[i-1][1]-s[i][k]});
            for(int j=2;j<=k;j++){
                int val=dp[i-1][j]-s[i][k]+s[i][j-1];
                while(!Q2.empty() && val>=Q2.back().val)Q2.pop_back();
                Q2.push_back(Point{j,val});
            }
            for(int j=k+1;j<=m;j++){
                while(!Q3.empty() && dp[i-1][j]>=Q3.back().val) Q3.pop_back();
                Q3.push_back(Point{j,dp[i-1][j]});
            }
            Q3.push_back(Point{m+1,0});
            for(int j=1;j<=m-k+1;j++){
                if (j-k) lmax=max(lmax,dp[i-1][j-k]);
                while(Q1.front().x<j-k+1)Q1.pop_front();
                while(!Q2.empty() && Q2.front().x<j+1)Q2.pop_front();
                while(Q3.front().x<j+k)Q3.pop_front();
                
                int val1=Q1.front().val+now1;
                if (!Q2.empty())val1=max(val1,Q2.front().val+now2);
                int val2=max(lmax,Q3.front().val);
                int val3=s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
                dp[i][j]=max(val1,val2)+val3;
                ans=max(ans,dp[i][j]);
                //cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
                now1+=a[i][j];
                int val=dp[i-1][j+1]-s[i][j+k]+s[i][j]-now1;
                while(!Q1.empty() && val>Q1.back().val)Q1.pop_back();
                Q1.push_back(Point{j+1,val});
    
                now2-=a[i][j+k];
                val=dp[i-1][j+k]-s[i][j+k]+s[i][j+k-1]-now2;
                while(!Q2.empty() && val>Q2.back().val)Q2.pop_back();
                Q2.push_back(Point{j+k,val});
            }
        }
        cout<<ans;
    }
    
    

    代码四 单调队列2 模仿std重写 (O(nm))

    #include<bits/stdc++.h>
    using namespace std;
    using vi=vector<int>;
    using vvi=vector<vi>;
    using pii=pair<int,int>;
    
    const int MAX_N = 50;
    const int MAX_M = 20000;
    const int MAX_K = 20000;
    
    int n,m,k;
    
    void calc(vi &a,vi &s, vi &dp, vi &newdp){
        deque<pii> Q;
        int now=0;
        for(int i=1;i<=m-k+1;i++){
            while(!Q.empty() && Q.front().first<i-k+1)Q.pop_front();
            now+=a[i-1];
            int val=dp[i]-s[i+k-1]+s[i-1]-now;
            while(!Q.empty() && Q.back().second<=val)Q.pop_back();
            Q.push_back({i,val});
            newdp[i]=Q.front().second+now;
        }
    }
    
    int main(){
        #ifndef ONLINE_JUDGE
        freopen("aa.in","r",stdin);
        #endif
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin>>n>>m>>k;
        vvi init_vvi(n+5,vi(m+5,0));
        vi init_vi(m+5,0);
        vi dp,dpl,dpr,newdpl,newdpr,lmax,rmax;
        vvi a,s,a_rev,s_rev;
        dp=dpl=dpr=newdpl=newdpr=lmax=rmax=init_vi;
        a=s=a_rev=s_rev=init_vvi;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                cin>>a[i][j];
                s[i][j]=s[i][j-1]+a[i][j];
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                a_rev[i][j]=a[i][m+1-j];
                s_rev[i][j]=s_rev[i][j-1]+a_rev[i][j];
            }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++)
                lmax[j]=max(lmax[j-1],dp[j]);
            for(int j=m;j>=1;j--)
                rmax[j]=max(rmax[j+1],dp[j]);
            calc(a[i],s[i],dpl,newdpl);
            calc(a_rev[i],s_rev[i],dpr,newdpr);
            for(int j=1;j<=m-k+1;j++){
                int val=s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
                dp[j]=max(lmax[max(j-k,0)],rmax[min(m+1,j+k)])+val;
                dp[j]=max(dp[j],newdpl[j]+val);
                dp[j]=max(dp[j],newdpr[m-j-k+2]+val);
                dpl[j]=dpr[m-j-k+2]=dp[j];
                //cout<<i<<" "<<j<<" "<<dp[j]<<endl;
            }
        }
        cout<<*max_element(dp.begin(),dp.end());
    }
    
    
  • 相关阅读:
    CSS动画DAY01
    第5章到8章小结
    实验9 根据材料编程
    实验5 编写、调试具有多个段的程序
    实验4 [bx]和loop的使用
    实验3 编程、编译、连接、跟踪
    第3章 寄存器(内存访问)小结
    实验2 用机器指令和汇编指令编程
    实验1 查看CPU和内存,用机器指令和汇编指令编程
    字符串生成及加密
  • 原文地址:https://www.cnblogs.com/mmmqqdd/p/12388796.html
Copyright © 2020-2023  润新知