zoukankan      html  css  js  c++  java
  • Codeforces Round #541 (Div. 2) 题解

    Codeforces Round #541 (Div. 2)

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

    A. Sea Battle

    题意:

    给出两个矩形的宽和高,满足第一个矩形的左上顶点为(0,0),右下顶点为(w1,-h1);第二个矩形的坐下顶点为(0,0),右上顶点为(w2,h2)。然后求围绕这两个矩形一圈的格子个数为多少。

    题解:

    利用二维前缀和的思路推导一下公式就行了,公式也有很多吧==我当时就是大概模拟了一下。。

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    ll w1,h1,w2,h2;
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);
        cin>>w1>>h1>>w2>>h2;
        ll ans = 0;
        ans+=(h1+h2+2)*(max(w1,w2)+2);
        ll now=min(w1,w2);
        ll tmp=max(w1,w2)-now;
        if(w1<w2) ans-=h1*tmp;
        else ans-=h2*tmp;
        ans-=(w1*h1+w2*h2);
        cout<<ans;
        return 0;
    }
    View Code

    B. Draw!

    题意:

    给出n次两个人的比分,每次给出xi,yi代表两个人的比分,然后问这两个人最多平局的多少次。

    题解:

    假设这两个人在比分为p的时候平局,对于xi,yi,xi+1,yi+1来说,肯定满足max(xi,yi)<=p<=min(xi+1,yi+1)的,然后就借用这个式子来算。注意一下xi==yi这种特殊情况,可能会被计算多次,处理一下就好了。

    原谅我代码对不上上面的题解。。懒得改了,我感觉我的做题思路还没有抽象化,上面一题也是,基本都是采用模拟的方法来做的,没有很好地进行数学建模。

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 10005;
    int n;
    int a[N],x[N],y[N];
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);
        cin>>n;
        ll ans = 1;
        for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
        ans+=min(x[1],y[1]);
        for(int i=2;i<=n;i++){
            if(x[i]>y[i]){
                int dx=x[i]-x[i-1],dy=y[i]-y[i-1];
                if(y[i]<x[i-1]) continue ;
                if(y[i-1]<x[i-1]) ans+=(y[i]-x[i-1]+1);
                else if(y[i-1]>x[i-1]) ans+=dy+1;
                else ans+=min(dx,dy);
            }else{
                int dx=x[i]-x[i-1],dy=y[i]-y[i-1];
                if(x[i]<y[i-1]) continue ;
                if(x[i-1]<y[i-1]) ans+=(x[i]-y[i-1]+1);
                else if(x[i-1]>y[i-1]) ans+=dx+1;
                else ans+=min(dx,dy);
            }
        }
        cout<<ans;
    }
    View Code

    C. Birthday

    题意:

    n个人,每个人都有对应的身高ai,然后这n个人站成一个圈,问怎样的站法,能够使相邻两个人的身高差最小,最后任意输出一种方法即可。

    题解:

    考虑贪心的做法(为啥这题的数据为100啊...),先对所有的数排个序,然后从前往后依次放奇数位置的人,从后往前依次放偶数位置的人。

    题解的证明用到了哈密顿回路,我们这种做法得到的答案为max(ai+2-ai),然后只需要证明不可能存在max(ai+1-ai)这样的答案就行了,证明这个在直观上面还是比较好理解的,很容易得证。

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 105;
    int a[N],ans[N];
    int n;
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        sort(a+1,a+n+1);
        int fir=1,last=n;
        for(int i=1;i<=n;i++){
            if(i&1){
                ans[fir++]=a[i];
            }else ans[last--]=a[i];
        }
        for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
        return 0;
    }
    View Code

    D. Gourmet choice

    题意:

    给出一个n*m的符号矩阵,分别对应第n个数和第m个数的大小关系,然后让你根据这个矩阵构造这n+m个数,如果得不到就输出"No"。

    题解:

    其实根据大小关系很容易想到拓扑排序,小的向大的连边,然后如果无环就存在一种情况。

    但是这里有等于符号,在本题中,等于的那些数本质上是相等的,也就是说拥有相同的拓扑序,那我们用并查集缩点处理即可。

    然后跑拓扑排序,途中标记答案,最后根据输出就行了。

    注意一下这种情况:就是如果一些点都属于一个集合中,但是有些点的关系存在不等关系,这就矛盾了,要判断一下。

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1005;
    int n,m,cnt,tot;
    char mp[N][N];
    int f[N<<1],num[N<<1],head[N<<1],in[N<<1],ans[N<<1];
    struct Edge{
        int u,v,next;
    }e[N*N<<1];
    int find(int x){
        return f[x]==x?f[x]:f[x]=find(f[x]);
    }
    void Union(int x,int y){
        int fx=find(x),fy=find(y);
        if(fx==fy) return ;
        f[fx]=fy;
    }
    void adde(int u,int v){
        e[tot].v=v;e[tot].next=head[u];head[u]=tot++;
    }
    int topsort(){
        queue <int> q;
        int tot=0;
        for(int i=1;i<=cnt;i++) if(!in[i]) q.push(i),tot++,ans[i]=1;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u];i!=-1;i=e[i].next){
                int v=e[i].v;
                if(--in[v]==0){
                    ans[v]=ans[u]+1;
                    q.push(v);
                    tot++;
                }
            }
        }
        return tot==cnt;
    }
    int main(){
        scanf("%d%d",&n,&m);
        int flag=0;
        for(int i=1;i<=n+m;i++) f[i]=i;
        for(int i=1;i<=n;i++){
            scanf("%s",mp[i]+1);
            for(int j=1;j<=m;j++){
                if(mp[i][j]=='=') Union(i,j+n);
                else flag=1;
            }
        }
        for(int i=1;i<=n+m;i++){
            int f=find(i);
            if(!num[f]) num[f]=++cnt;
        }
        if(cnt==1 && flag){
            cout<<"No";
            return 0;
        }
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int u=num[find(i)],v=num[find(j+n)];
                if(u==v&&mp[i][j]!='='){
                    cout<<"No";
                    return 0;
                }
                if(mp[i][j]=='<') adde(u,v),in[v]++;
                else if(mp[i][j]=='>') adde(v,u),in[u]++;
            }
        }
        int f=topsort();
        if(!f) cout<<"No";
        else{
            puts("Yes");
            for(int i=1;i<=n+m;i++){
                int fx=find(i);
                cout<<ans[num[fx]]<<" ";
                if(i==n) cout<<'
    ';
            }
        }
        return 0;
    }
    View Code

    F. Asya And Kittens

    题意:

    有n个数乱序排列,每个数都互不相等,并且它们中间有n-1个隔板将它们分离。现在每次会抽出两个数之中的隔板,进行n-1次操作,最终n个数之中没有隔板,最后输出这n个数排列的一种情况。注意题目中给出的两个数,不一定是相邻的,但它们一定在相邻的两个块中。

    题解:

    对于两个相邻的块,我们直接合并就行了,往左或者右合并都行,因为反正它们都会成为一个块。这里用并查集的话就要启发式合并,减少空间的消耗,具体点就是将size小的集合往size大的集合合并,这样可以节约总空间,并且可以证明空间复杂度不超过O(nlogn);如果不这样合并,空间复杂度上界可能为O(n^2)。

    当然这里有个黑科技rope,用这个来储存十分节约空间,对于这个题怎么合并都行,并且单次插入、删除等操作都是O(logn)的复杂度,常数稍微大了点,但是已经十分优秀了。这个底层是用平衡树来搞的,似乎可以来代替块状链表= =反正就是个类似于链表的东西。

    代码如下:

    #include <bits/stdc++.h>
    #include <ext/rope>
    using namespace std;
    using namespace __gnu_cxx;
    typedef long long ll;
    const int N = 150005;
    int n;
    int f[N];
    rope <int> a[N];
    int find(int x){
        return f[x]==x ? f[x] : f[x]=find(f[x]);
    }
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);
        cin>>n;
        for(int i=1;i<=n;i++) f[i]=i,a[i]=i;
        for(int i=1;i<n;i++){
            int x,y;
            cin>>x>>y;
            int fx=find(x),fy=find(y);
            if(fx==fy) continue ;
            f[fx]=fy;
            a[fy]+=a[fx];
        }
        for(int i=1;i<=n;i++){
            int f=find(i);
            if(f==i){
                for(int j=0;j<a[f].size();j++){
                    cout<<a[f][j]<<" ";
                }
                break ;
            }
        }
        return 0;
    }
    View Code

    最后再口胡一下E题吧...E题就是首先定义字符串的" * "操作,s*t=t+s1+t+s2+....+sn+t,然后给出n个字符串,最后问最终的字符串中,连续的字符最多有多少个?

    感觉这题也还是挺有意思的,可以用递推来解:设f[i][j]表示为前i个字符串,字符j的最长连续字符的个数。那么到第i+1个字符串时,首先求出它的前后缀,然后分情况转移:

    1.当前串的所有字符都相同;2.连续的前缀和连续的后缀相等;3.连续的前缀和连续的后缀不相等,这个应该还是比较好求了。

    这是从前往后递推的,然后也可以从后往前递归,题解说的是可以证明这个结合律是合法的,然后从后往前,其实思想都差不多。

  • 相关阅读:
    sqlite错误 Abort due to constraint violation column id is not unique id没开启自动增长
    字符串转为日期类型
    XPTable 一行添加数据 如果想添加多行 可以使用for循环
    在逮捕异常的时候 可以获取e.MESSAGE里面的信息 然后判断是什么异常
    C# 加载图片image (C#)Image.FromFile 方法会锁住文件的原因及可能的解决方法
    计算两个时间的前后 时间戳
    用C#语言写的多线程演示程序:两个线程,可以开始,可以暂停,可以恢复,可以清除。
    sqlite插入日期时候 出现18991230 0:00:00
    datagridview绑定dataset的时候 需要这一句
    WinForm 子线程修改主线程(UI线程)Control 【Z】
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10428699.html
Copyright © 2011-2022 走看看