Codeforces Round #656 (Div. 3)总结

题目链接
感受:这次一共做出四道题。感觉自己的思维还是不那么灵活,在A题卡了半小时(丢人)。前二十分钟看的A题感觉有思路又感觉没思路,索性直接看B,两分钟AC了,然后又看了两分钟A,还是写不出,又看了C,做了十分钟AC 了,这时候看了眼D,觉得应该出的不会快,然后决定静心看A,好好分析了几分钟AC了。这时候还一个小时,先是看了一眼E题,一个图论题,一开始的想法是并查集判环,但写到一半发现我处理不了一些细节,然后就放弃了去看D。D题给我的感觉是像一个贪心,分析了一下发现当前决策会影响下面的决策,所以贪心否定了。那么只剩下暴力和dp了,但dp的话免不了遍历所有情况,与其dp不如直接暴力,然后写了个搜索,看到数据量有点大,怕超时。分析了十分钟,发现一直递归下去到停止条件的话,2e5的数据量顶多递归二十多层,这种复杂度是可以接受的, O ( n l o g n ) O(nlogn) O(nlogn)的复杂度交上去AC了。。当即就感觉D题好水。。。然后直接睡觉了。。
总结:有些图论题可能是没有系统训练的缘故把,好多细节我处理不了,然后对时间复杂度的分析也很重要,如果不分析的话那个D题我肯定不会交的。稍微分析一下发现是可行的。

A题

题目大意:给你三个数x,y,z。让你求三个数a,b,c。条件是x=max(a,b),y=max(a,c),z=max(b,c).存在这三个数就输出yes并输出这些书,否则no
思路:小小分析一波,因为输出顺序是任意的,所以我们只需要按自己的意愿去构造就行了,把xyz从大到小排序。首先x是ab最大值,y是ac最大值,都含有a,我们假设a最大,所以x一定等于y。b和c一定小于a,所以把z赋给b,那么c就任意取一个最小值就行了,取1就ok了。
一开始卡在这个题也是自己分析不到位把。

#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+10;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int x,y,z;
        cin>>x>>y>>z;
        if((x>y&&x>z)||(y>x&&y>z)||(z>x&&z>y))
        {
            cout<<"NO"<<endl;
            continue;
        }
        if(x<y) swap(x,y);
        if(x<z) swap(x,z);
        if(y<z) swap(y,z);
        int a,b,c;
        a=x;b=z;c=1;
        cout<<"YES"<<endl;
        cout<<a<<" "<<b<<" "<<c<<endl;
    }
    return 0;
}

B题

题目大意:给你两个已经相互插入的数字序列,让你求出最原始的那个数字序列。
思路:这个很简单,因为这些数字序列顺序的固定的,所以我们从左往右扫过去,对于没出现过的数字我们就把他扔到答案里,然后标记这个数字出现过。

#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+10;
int a[N],ans[N];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        map<int,int>f;
        int n,cnt=0;
        cin>>n;
        for(int i=1;i<=2*n;i++) cin>>a[i];
        for(int i=1;i<=2*n;i++)
        {
            if(!f[a[i]])
            {
                f[a[i]]=1;
                ans[++cnt]=a[i];
            }
            if(cnt==n) break;
        }
        for(int i=1;i<=cnt;i++) cout<<ans[i]<<" ";
        cout<<endl;
    }
    return 0;
}

C题

题目大意:给你一个序列,问最少去掉多少个前缀元素能得到一个“好序列”。“好序列”是这样定义的:每次能从一个序列的左右两边取数,若能取出一个非递减序列则是“好序列”。
思路:先分析什么样的序列能够叫做好序列,首先假设这个序列是b1,b2,…bm…bn。那么bm一定是最大的元素,然后从左到右是非减序列,从bm到bn是非增序列。为什么呢,假设b2

#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+10;
int a[N];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        int right=n;
        while(a[right-1]>=a[right]&&right>=1) right--;
        //cout<
        while(a[right-1]<=a[right]&&right>=1) right--;
        int ans=right-1;//这个式子是最后化简出来的一个最简的式子。
        if(ans==-1) ans=0;
        cout<<ans<<endl;
    }
    return 0;
}

D题

题目大意:好难描述啊。首先给你一个字符串,让你用最少的操作将他变成“a-好字符串”。那么什么是“a-好字符串”呢。就是前半部分全是a,后半部分是“(a+1)-好字符串”,然后依次递推下去。具体还是看题目里的解释吧。
思路:一眼看上去肯定觉得是个贪心,每次将拥有当前需要字符最多的那边变成指定字符,但是这样是不行的,不满足无后效性。所以考虑dp和暴力,但我们都知道dp也可以算是一种较为优雅的暴力,与其dp不如直接深搜呢。所以我们可以枚举所有情况,最后取最小值就行了。具体的还是代码写的清楚。
这个题教会我们的应该不是思路,而是对复杂度的分析,不分析乍一看很像超时代码,实则不然。2e5的数据量每次递归一半,最多递归二十多层nlogn的复杂度完全可以。

#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+10;
char s[N];
int solve(int l,int r,char m)
{
    if(l==r)
    {
        if(s[l]==m) return 0;
        else return 1;
    }
    int mid=(l+r)>>1,left=0,right=0,len=r-l+1;
    for(int i=l;i<=mid;i++) if(s[i]==m) left++;
    for(int i=mid+1;i<=r;i++) if(s[i]==m) right++;
    int ans=min(len/2-left+solve(mid+1,r,m+1),len/2-right+solve(l,mid,m+1));
    return ans;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        char m='a';
        cin>>n;
        for(int i=1;i<=n;i++) cin>>s[i];
        int ans=solve(1,n,m);
        cout<<ans<<endl;
    }
    return 0;
}

E题

题目大意:给你一个n个顶点m条边的图,其中一些边没有反向,其余边有方向,让你对每一条边赋一个方向,要求最终图不能形成自环。(没有重边)
题解

你可能感兴趣的:(codeforces,字符串,算法,数据结构)