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());
}

原文地址:https://www.cnblogs.com/mmmqqdd/p/12388796.html

时间: 2024-10-06 23:06:01

Codeforces Round #620(Div. 2)的相关文章

Codeforces Round #620 (Div. 2)D dilworld定理

题:https://codeforces.com/contest/1304/problem/D 题意:给定长度为n-1的只含’>'和‘<’的字符串,让你构造出俩个排列,俩个排列相邻的数字之间要满足这个字符串,找出的俩个要是最小化最长上升子序列,和最大化最长上升子序列: 分析:dilworld定理,最长下降子序列个数等于最长上升子序列的长度 先说构造最小的,LIS的长度就等于最长的连续'<'的长度加1,贪心地把最大的数放在最前面: 构造最大的就把最小的数尽可能放在前面 原文地址:https

Codeforces Round #620 (Div. 2)F2

题意:给出n,和m表示有n天,m块区域,每块区域都有一定数论的动物数量,k表示可以在这一天中观察[x,max(x+k-1,m)]的区域内的动物,有俩台相机,一台只能在偶数天用,另一台则是在奇数天用,每用一次就得在那个区域内待俩天,相邻的要是有重复的区域,该区域内的动物数只计数一次,问最多有可能的动物数目是多少 分析:因为n<=50,m<=20000,所以我们考虑一下dp[n][m],dp[i][j]表示:在第 i 天选择[j,j+k-1]区域拍摄的最大拍摄量. 因为是连续拍摄俩天,所以我们可以

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿

Codeforces Round #424 (Div. 2) C. Jury Marks(乱搞)

题目链接:Codeforces Round #424 (Div. 2) C. Jury Marks 题意: 给你一个有n个数序列,现在让你确定一个x,使得x通过挨着加这个序列的每一个数能出现所有给出的k个数. 问合法的x有多少个.题目保证这k个数完全不同. 题解: 显然,要将这n个数求一下前缀和,并且排一下序,这样,能出现的数就可以表示为x+a,x+b,x+c了. 这里 x+a,x+b,x+c是递增的.这里我把这个序列叫做A序列 然后对于给出的k个数,我们也排一下序,这里我把它叫做B序列,如果我

[Codeforces] Round #352 (Div. 2)

人生不止眼前的狗血,还有远方的狗带 A题B题一如既往的丝帛题 A题题意:询问按照12345678910111213...的顺序排列下去第n(n<=10^3)个数是多少 题解:打表,输出 1 #include<bits/stdc++.h> 2 using namespace std; 3 int dig[10],A[1005]; 4 int main(){ 5 int aa=0; 6 for(int i=1;;i++){ 7 int x=i,dd=0; 8 while(x)dig[++dd

Codeforces Round #273 (Div. 2)

Codeforces Round #273 (Div. 2) 题目链接 A:签到,仅仅要推断总和是不是5的倍数就可以,注意推断0的情况 B:最大值的情况是每一个集合先放1个,剩下都丢到一个集合去,最小值是尽量平均去分 C:假如3种球从小到大是a, b, c,那么假设(a + b) 2 <= c这个比較明显答案就是a + b了.由于c肯定要剩余了,假设(a + b)2 > c的话,就肯定能构造出最优的(a + b + c) / 3,由于肯定能够先拿a和b去消除c,而且控制a和b成2倍关系或者消除

Codeforces Round #339 (Div. 2) B. Gena&#39;s Code

B. Gena's Code It's the year 4527 and the tanks game that we all know and love still exists. There also exists Great Gena's code, written in 2016. The problem this code solves is: given the number of tanks that go into the battle from each country, f

Codeforces Round #315 (Div. 1)

A. Primes or Palindromes? time limit per test 3 seconds memory limit per test 256 megabytes input standard input output standard output Rikhail Mubinchik believes that the current definition of prime numbers is obsolete as they are too complex and un