Educational Codeforces Round 123 (Rated for Div. 2)思路分享

Educational Codeforces Round 123 (Rated for Div. 2)

本来这场的状态不太好,到后面磕不出来的时候有点瞌睡了,到最后竟然没有记rating....(本来还指望着通过这场掉一波分,这样就能报下一场的div2了,自我感觉div2现在还没有到拿捏的程度,E,F就没有当场做出来的时候....)

A. Doors and Keys

A题算是签到题吧,只需要判断钥匙与门的先后顺序即可。

B. Anti-Fibonacci Permutation

B题要求构造一个排列使得他为非斐波那契数列,要求构造出b个排列。
首先想到的就是倒序,之后发现我们将1依次向左移即可。
即:
4 3 2 1.
4 3 1 2
4 1 3 2
1 4 3 2
因为除了1之外,其他的元素都是倒序,注定符合\(a_i+a_{i+1}>a_{i+2}\)

C. Increase Subarray Sums

我承认我被这个题卡了许久,将近50分钟左右....
由于n是5000的缘故,所以我们完全可以枚举所有的区间去统计答案。可以发现对于某个\(f(k)\)而言,它的答案只可能由以下部分组成:
1.什么都不选,即答案为0.
2.选取区间长度恰好为k的区间,并将这个区间内的每个数都加上x。
3.选取区间长度小于k的区间,并将这个区间内的每个数都加上x。
4.选取区间长度大于k的区间,并将这个区间内的和增加kx.
第一部分不用考虑,我们只需要在最后的时候和0比大小即可。第二部分考虑我们枚举所有的区间的时候就可以办到了。第三部分其实就是f(0),f(1),f(2),...,f(k-1)取最大值,其实也好维护。
接下来就是第4部分了。我们令g(k)表示区间长度为k的区间的最大值。那么第四部分也好考虑就是max(g(k+1),g(k+2),...,g(n))+k
x;
在我们枚举所有区间的时候后一方面维护第二部分的值,一方面维护g的即可。
但其实我们发现不用这么麻烦,我们完全可以正着做一次,倒着做一次dp即可。正着的不多解释,关于倒着的,考虑如果x的对应的最优区间长度为y的话,则x+1对应的长度也为y。因为sum(y)+kx>sum(z)+kx(z为x+1对应的最优区间长度).则对于f(x+1)的时候,显然存在sum(y)+(k+1)x>sum(z)+(k+1)x.我们完全可以只从x+1出转移即可。具体的可以参考代码:

点击查看代码
#include<bits/stdc++.h> 
#define ll long long
#define db double 
using namespace std;
const int N=5010;
int n,T,m,a[N];
ll b[N];
int main()
{
//    freopen("1.in","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        memset(b,0xef,sizeof(b));
        for(int l=1;l<=n;++l)
        {
            ll sum=0; 
            for(int r=l;r<=n;++r)
            {
                sum+=a[r];
                b[r-l+1]=max(b[r-l+1],sum+(r-l+1)*m);
            }
        }
        for(int i=1;i<=n;++i) b[i]=max(b[i],b[i-1]);
        for(int i=n;i>=0;--i) b[i]=max(b[i],b[i+1]-m);
        for(int i=0;i<=n;++i) printf("%lld ",max(b[i],0ll));
        puts("");
    }
    return 0;
}

D. Cross Coloring

这个题其实想到的话真的也不难。
每次涂颜色都会将一个行,一个列涂成同一个颜色。考虑每次操作我们都有k种选择,总的来说答案就是\(k^q\)。但是会有特殊的情况,即假设我在某次操作中,涂了1行1列,之后这些元素全部被其他颜色覆盖了,那我这次操作就没有任何意义了。所以我们只需要将这些操作删选出来即可。可以采用倒序的方法,记录下哪些行,列已经被涂过了,以及是否所有的行列都被涂过了。

点击查看代码
#include<bits/stdc++.h> 
#define ll long long
#define db double 
using namespace std;
const int N=2e5+10,P=998244353;
int n,T,m,q,k,vish[N],visl[N],x[N],y[N];
inline ll power(ll x,ll y)
{
    ll ans=1;
    while(y)
    {
        if(y&1) ans=ans*x%P;
        y>>=1;
        x=x*x%P;
    }
    return ans%P;
}
int main()
{
//    freopen("1.in","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d",&n,&m,&k,&q);
        for(int i=1;i<=q;++i) scanf("%d%d",&x[i],&y[i]);
        for(int i=1;i<=max(n,m);++i) vish[i]=visl[i]=0;
        int cnt=0,cnth=0,cntl=0;
        for(int i=q;i>=1;--i)
        {
            if((cntl==m||vish[x[i]])&&(cnth==n||visl[y[i]])) continue;
            ++cnt;
            if(!vish[x[i]]) vish[x[i]]=1,cnth++;
            if(!visl[y[i]]) visl[y[i]]=1,cntl++;
        }
        printf("%lld\n",power(k,cnt));
    }
    return 0;
}

E. Expand the Path

怎么说,这个题到最后还是没什么想法.....(还是我太菜了,又菜又爱生气.....)
之前的想法一直是全部的路径叠加到一起看有没有什么规律....发现没有(我太菜了..)....
但其实还有一种想法,就是我们可以统计下我们最多添加多少个D,添加多少个R。然后沿着原路径,对于每一个点来说如果他前面(包括它自己)只有D或只有R的话,说明我们在这个点的基础上将以他为端点的一行或一列的\(n-cnt_d或n-cnt_r\)个格点都覆盖掉,很简单的,我们可以在其基础上倍增D或R即可。之后在经过D与R之后的情况,我们可以通过倍增达到的区域就变成了一个\((n-cnt_d)\times(n-cnt_r)\)的长方形,每个点都对应一个矩形,我们的目的就是求所有的矩形的并。这可以直接采用扫描线的模板即可(正好我最怵这个东西,这次再写一遍..)。
PS:补一下为什么还有这种想法,我们考虑原路径中,我们中途加了p次R,k次D,到达了某个点(x,y).那么假如说我们没有增加这R和D的情况下,我们本应到达的点应为(x-k,y-p).而p,k则由我们能添加的最大R和D的关系限制。所以这种方法和题意中是一一对应的。换言之,题目中能通过的所有的点,一定是在原路径上的某个点之前(包括自身),通过扩展D和R来达到的。现在我们枚举所有的点,将它通过扩展能到达的所有点的并一定是答案。

点击查看代码
#include<bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
#define ll long long 
using namespace std;
const int N=2e5+10;
char c[N];
int T,n,m,b[N<<1],num;
struct wy{int x,y1,y2,id;}a[N<<2];
struct Tree
{
    int l,r,len,dat;
    #define l(p) t[p].l
    #define r(p) t[p].r
    #define len(p) t[p].len
    #define dat(p) t[p].dat
}t[N*20];
inline bool cmp(wy a,wy b) {return a.x<b.x;}
inline void build(int p,int l,int r)
{
    l(p)=l;r(p)=r;
    len(p)=0;dat(p)=0;
    if(l==r) return;
    int mid=l+r>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
}
inline int find(int x) {return lower_bound(b+1,b+num+1,x)-b;}
inline void pushup(int p)
{
    if(l(p)==r(p))
    {
        len(p)=dat(p)?b[l(p)+1]-b[l(p)]:0;
        return;
    }
    if(dat(p)) len(p)=b[r(p)+1]-b[l(p)];
    else len(p)=len(ls)+len(rs);
}
inline void alter(int p,int l,int r,int x)
{
    if(l<=l(p)&&r>=r(p))
    {
        dat(p)+=x;
        pushup(p);
        return;
    }
    int mid=l(p)+r(p)>>1;
    if(l<=mid) alter(ls,l,r,x);
    if(r>mid)  alter(rs,l,r,x);
    pushup(p);
}
int main()
{
//    freopen("1.in","r",stdin); 
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%s",&n,c+1);
        m=strlen(c+1);
        int cntr=0,cntd=0;
        for(int i=1;i<=m;++i)
        {
            if(c[i]=='D') cntd++;
            else cntr++;
        }
        bool f1=false,f2=false;
        int x=1,y=1;num=0;
        for(int i=1;i<=m;++i)
        {
            if(c[i]=='D') f1=true,x++;
            if(c[i]=='R') f2=true,y++;
            int j=2*i-1,k=2*i;
            a[j].x=y;a[j].y1=x;a[j].y2=x+(f1?n-cntd:1);a[j].id=1;
            a[k].x=y+(f2?n-cntr:1);a[k].y1=x;a[k].y2=x+(f1?n-cntd:1);a[k].id=-1;
            b[++num]=x;b[++num]=x+(f1?n-cntd:1);
        }
        sort(a+1,a+2*m+1,cmp);
        sort(b+1,b+num+1);
        num=unique(b+1,b+num+1)-b-1;
        build(1,1,num-1);
        alter(1,find(a[1].y1),find(a[1].y2)-1,a[1].id);
        ll ans=0;
        for(int i=2;i<=2*m;++i)
        {
            ans+=(ll)len(1)*(a[i].x-a[i-1].x);
            alter(1,find(a[i].y1),find(a[i].y2)-1,a[i].id);
        }
        printf("%lld\n",ans+1);
    }
    return 0;
}
posted @ 2022-02-23 18:07  逆天峰  阅读(115)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//