欢迎来到蒟蒻mqd的博客

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]<<" \n"[i==n];
        for(int i=1;i<n;i++)s[i]^='<'^'>';
        solve();
        for(int i=1;i<=n;i++)cout<<n+1-ans[i]<<" \n"[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());
}

posted @ 2020-03-01 11:28  mmqqdd  阅读(179)  评论(0编辑  收藏  举报