• Educational Codeforces Round 93 (Rated for Div. 2) 简要题解


    A

    根据“两短边之和大于第三边”,可以直接判断(a_1,a_2,a_n)的大小关系即可。

    int n,a[N];
    
    int main()
    {
        int T=read();
        while (T--)
        {
            n=read();
            rep(i,1,n) a[i]=read();
            sort(a+1,a+1+n);
            int x=a[1],y=a[2],z=a[n];
            if (x+y>z) puts("-1");
            else printf("%d %d %d
    ",1,2,n);
        }
        return 0;
    }
    

    B

    取一段0的话会使得两段1连在一起,显然会帮助对面得到更多的1,所以贪心策略就是按照极长连续1的长度从大到小贪心的取。

    int n,m,a[N];
    char s[N];
    
    int main()
    {
        int T=read();
        while (T--)
        {
            m=0;
            scanf("%s",s+1);
            n=strlen(s+1);
            int cnt=0;
            rep(i,1,n)
            {
                if (s[i]=='0')
                {
                    if (cnt) a[++m]=cnt;
                    cnt=0;
                }
                else cnt++;
            }
            if (cnt) a[++m]=cnt;
            sort(a+1,a+1+m);
            int ans=0;
            for (int i=m;i>=1;i-=2) ans+=a[i];
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    C

    记前缀和为(S_i),由题知有(S_r-S_{l-1}=r-l+1),移项得(S_r-r=S_{l-1}-(l-1)),用一个map记录(S_i-i)的值的个数即可。

    int n;
    map<int,int> mp;
    char str[N];
    
    int main()
    {
        int T=read();
        while (T--)
        {
            n=read();scanf("%s",str+1);
            mp.clear();mp[0]=1;
            int sum=0;ll ans=0;
            rep(i,1,n)
            {
                sum+=(str[i]-'0');
                ans+=mp[sum-i];
                mp[sum-i]++;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    D

    考虑四条边(a,b,c,d)满足(a<b<c<d),经过一些简单的验证可以得到两个矩形面积和为(ab+cd)时最大(即配对的边不会有交叉),于是可以直接dp.

    int n1,n2,n3,a[210],b[210],c[210];
    long long f[210][210][210];
    
    int main()
    {
        n1=read();n2=read();n3=read();
        rep(i,1,n1) a[i]=read();
        rep(i,1,n2) b[i]=read();
        rep(i,1,n3) c[i]=read();
        sort(a+1,a+1+n1);sort(b+1,b+1+n2);sort(c+1,c+1+n3);
        f[0][0][0]=0;
        rep(i,0,n1) rep(j,0,n2) rep(k,0,n3)
        {
            if (i) f[i][j][k]=max(f[i][j][k],f[i-1][j][k]);
            if (j) f[i][j][k]=max(f[i][j][k],f[i][j-1][k]);
            if (k) f[i][j][k]=max(f[i][j][k],f[i][j][k-1]);
            if ((i) && (j)) f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k]+a[i]*b[j]);
            if ((i) && (k)) f[i][j][k]=max(f[i][j][k],f[i-1][j][k-1]+a[i]*c[k]);
            if ((j) && (k)) f[i][j][k]=max(f[i][j][k],f[i][j-1][k-1]+b[j]*c[k]);
        }
        long long ans=f[n1][n2][n3];
        printf("%lld",ans);
        return 0;
    }
    

    E

    记0类武器有(n_0)个,1类武器有(n_1)个,我们需要维护出对当前(n_0+n_1)个武器中伤害的前(n_1)大值以进行加倍,但是要注意如果这些应该被加倍的武器全部是1类的话,就需要拿走一个最小的,同时在剩余的武器中取出一个最大的。使用两个set维护前(n_1)大和非前(n_1)大,注意到一次修改操作给两个set带来的插入和删除操作是(O(1))个的,于是可以直接维护,具体细节见下。

    set<pii> s0,s1;
    
    bool chk()
    {
        if ((s0.empty()) || (s1.empty())) return 0;
        pii x=*s0.begin(),y=*s1.rbegin();
        return x.fir<y.fir;
    }
    
    int main()
    {
        int q=read();
        ll all=0,sum0=0;
        int n10=0,n1=0,n0=0;
        while (q--)
        {
            int tp=read(),d=read(),op=1;
            all+=d;
            pii now=mkp(d,tp);
            if (d<0)
            {
                d=-d;op=-1;now.fir*=-1;
                if (s0.find(now)!=s0.end()) 
                {
                    s0.erase(now);sum0-=d;
                    if (now.sec) n10--;
                }
                else s1.erase(now);
            }
            else s1.insert(now);
            if (tp) n1+=op;
            else n0+=op;
            //cout << "now " << n1 << " " << (int)s0.size() << " " << all << endl;
            while (n1<(int)s0.size())
            {
                pii x=*s0.begin();s0.erase(x);sum0-=x.fir;
                if (x.sec) n10--;
                s1.insert(x);
            }
            while (n1>(int)s0.size())
            {
                if (s1.empty()) break;
                pii x=*s1.rbegin();s1.erase(x);
                s0.insert(x);sum0+=x.fir;
                if (x.sec) n10++;
            }
            while (chk())
            {
                pii x=*s0.begin(),y=*s1.rbegin();
                //cout << "change " << x.fir << " " << y.fir << endl;
                if (x.sec) n10--;
                s0.erase(x);s1.erase(y);
                sum0-=x.fir;sum0+=y.fir;
                s0.insert(y);s1.insert(x);
            }
            ll nows=sum0;
            if ((n10==n1) && (n1))
            {
                pii x=*s0.begin();nows-=x.fir;
                if (!s1.empty())
                {
                    x=*s1.rbegin();nows+=x.fir;
                }
            }
            printf("%lld
    ",nows+all);
        }
        return 0;
    }
    

    F

    有一个显然的贪心是:枚举(k),然后从位置1开始、每次找一个最小的,满足能形成连续(k)个0/1的位置跳过去。

    根据调和级数那套理论,对于每个(k)如果我们能在处于任何位置时,能够用(f(k))找到下一步的起点,那么总的时间复杂度是(O(f(k) imes nlog n)).

    首先可以预处理(nxt_{i,0/1})表示从i开始往后走的极长0/1段的长度。之后有一个比较暴力的思路是对当前的起点每次二分一个终点,check的话就需要考虑这个区间内(nxt_{i,0/1})的最大值是否(geq k),时间复杂度是(O(nlog^2n)), 赛时实测会TLE.

    但是由于我们枚举的(k)是单调递增的,所以可以用并查集维护以当前点为起点时的最近终点是谁。这样的话就需要在对(k)做完答案后将(max(nxt_{i,0/1})=k)(i)连向下一个位置,于是每次查询终点近乎线性,可以通过。

    int n,nxt[N][2],fa[N],a[N];
    char s[N];
    vi val[N];
    
    int find(int x)
    {
        if (fa[x]==x) return x;
        fa[x]=find(fa[x]);
        return fa[x];
    }
    
    int main()
    {
        //freopen("a.in","r",stdin);
        //freopen("a.out","w",stdout);
        n=read();
        scanf("%s",s+1);
        per(i,n,1)
        {
            if (s[i]!='1') nxt[i][0]=nxt[i+1][0]+1;
            if (s[i]!='0') nxt[i][1]=nxt[i+1][1]+1;
            a[i]=max(nxt[i][0],nxt[i][1]);
            val[a[i]].pb(i);
        }
        //rep(i,1,n) cout << a[i] << " ";cout << endl;
        rep(i,1,n+1) fa[i]=i;
        rep(k,1,n)
        {   
            int p=1,cnt=0;
            while (p+k-1<=n)
            {
                int nxt=find(p);
                if (nxt<=n)
                {
                    p=nxt+k;cnt++;
                }
                else break;
            }
            printf("%d ",cnt);
            int len=val[k].size();
            rep(i,0,len-1)
            {
                int x=val[k][i],y=x+1;
                int fx=find(x),fy=find(y);
                fa[fx]=fy;
            }
            //rep(i,1,n) cout << find(i) << " ";cout << endl;
        }
        return 0;
    }
    

    G

    写完F,读懂G之后就会做了,但是没啥时间写了,着实自闭。

    考虑求是否存在一个周长为(C)的矩形,后面可以用调和级数的复杂度求出最大因数。

    去掉边长为(y)的边后其实就是求一个集合(S={a_i-a_j|0leq i,jleq n,a_i>a_j})中的元素,记(b_i)表示是否有(a_j=i),那么有(c_k=sum_{i=0}^{x-k}b_ib_{i+k}),这是一个经典的卷积形式,可以将(b)翻转得到(c_k=sum_{i=0}^{x-k}b_ib_{x-(i+k)}'),由于值域不大直接NTT即可。

    int n,x,y,a[N],b[N],r[N],f[N],ans[N];
    
    void ntt(int lim,int *a,int typ)
    {
        rep(i,0,lim-1)
            if (i<r[i]) swap(a[i],a[r[i]]);
        for (int mid=1;mid<lim;mid<<=1)
        {
            int gn=qpow(3,(maxd-1)/(mid<<1)),len=(mid<<1);
            if (typ==-1) gn=getinv(gn);
            for (int sta=0;sta<lim;sta+=len)
            {
                int g=1;
                for (int j=0;j<mid;j++,g=mul(g,gn))
                {
                    int x=a[sta+j],y=mul(a[sta+j+mid],g);
                    a[sta+j]=add(x,y);a[sta+j+mid]=dec(x,y);
                }
            }
        }
        if (typ==-1)
        {
            int invn=getinv(lim);
            rep(i,0,lim-1) a[i]=mul(a[i],invn);
        }
    }
    
    int calcr(int len)
    {
        int lim=1,cnt=0;
        while (lim<len) {lim<<=1;cnt++;}
        rep(i,0,lim-1) r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1)));
        return lim;
    }
    
    int main()
    {
        n=read();x=read();y=read();
        rep(i,0,n)
        {
            int x=read();a[x]=1;
        }
        rep(i,0,x) b[x-i]=a[i];
        int lim=calcr((x+1)<<1);
        ntt(lim,a,1);ntt(lim,b,1);
        rep(i,0,lim-1) a[i]=mul(a[i],b[i]);
        ntt(lim,a,-1);
        rep(i,1,x) 
        {
            f[i]=a[x+i];
            //if (f[i]) cout << i << " ";
        }
        //cout << endl;
        rep(i,0,M) ans[i]=-1;
        rep(i,0,x)
        {
            if (!f[i]) continue;
            int c=(y+i)*2;
            //cout << "now " << c << endl;
            for (int j=c;j<=M;j+=c) ans[j]=max(ans[j],c);
        }
        int q=read();
        while (q--)
        {
            int x=read();
            printf("%d ",ans[x]);
        }
        return 0;
    }
    
  • 相关阅读:
    CAD中导入.pat文件
    使用solid works 助力NBA | 操作案例
    Java关键字---this的由来和其三大作用
    关于solid works中的:动态链接库(DLL)初始化例失败的解决方法
    基于51单片机的keli安装方法
    wintc的安装方法
    文件处理2
    文件处理1
    CAD绘制篮球教程
    数据分析之Numpy
  • 原文地址:https://www.cnblogs.com/encodetalker/p/13509764.html
Copyright © 2020-2023  润新知