Codeforces Global Round 16 A-E 部分题解

A-Median Maximization

题意

        给出n和s,求n个数字和为s的情况下,求中位数的最大值。

思路

         签到题,贪心,中位数前面全为0,后面都平均下来,如果不是整数,那么就在给后面让它最大

代码

        当时是用的隐藏掉的部分,因为时间不等人,分情况百分百对就没有考虑那么多了...好吧是我菜了

inline void Case_Test()
{
    cin>>n>>s;
    //if (n%2==1) n-=n/2;
    //else n-=(n-1)/2;
    n-=(n-1)/2;
    cout<<s/n<<endl;
}

B-MIN-MEX Cut

题意

        MEX就是没出现的最小数字,给一个01串,可以将这个字符串求最小的MEX和是多少。

思路

        我们可以看出,如果有"01"的话,那么就是2,这样划不来,实际上我们可以将一个"01"直接分成"0"和"1",这样的话答案只贡献了1(1+0),因为MEX("1")=0,那么我们就将1分割开来,看有多少个"01",但是最后要取min(2,ans),因为答案最多才是2,不可能比2还多,因为我们什么也不选,选择原串才到2,只出现了01,MEX(s)=2

代码

inline void Case_Test()
{
    cin>>s;
    l=s.length();
    ans=0;
    if (s[0]=='1') ans+=0,pre=1;
    else ans+=1,pre=0;
    //cout<<ans<<" "<<pre<<endl;
    for (int i=1;i<l;i++)
    {
        tmp=s[i]-'0';
        if (tmp==pre) continue;
        if (tmp==1) // tmp=0 
            ans+=0,pre=1;
        else //tmp = 1
            ans+=1,pre=0;
    }
    cout<<min(ans,2)<<endl;
}

C-MAX-MEX Cut

题意

        给出一个两行的字符串,跟B很像,可以将原串分为若干个两行子串,求最大的MEX和,好吧,大致是这个意思。

思路

        这个两行好难打,还得用latex,我还是简单写吧,还是按照B题一样看,一行一行相邻的比较,大致就是遇到一列是"01"的话就直接ans+=2,因为贪心嘛,要求最大的MEX和,那么遇到能凑2的就凑2...然后我当时做题,就是前一列是{00,01,11}和后一列是{00,01,11}一共三三得九种情况,然后判断,大致就能贪心出来了,比如如果前一列是"00"后一列是"11",那么这个时候能凑出2了,然后清零(具体清零操作就是pre=-1)

        但是注意一点,如果是"00"遇到"01",我这里wa了一发,就是这时候得分开算,ans+=3,因为可以分成两个,前面"00"贡献是1后面的"01"贡献是2。

代码

inline void Case_Test()
{
    int ans=0;
    cin>>n>>s1>>s2;
    for (int i=0;i<n;i++)
        a[i]=s1[i]+s2[i]-'0'-'0';
    pre=a[0];
    if (a[0]==1) ans+=2,pre=-1;
    //for (int i=0;i<n;i++) cout<<a[i]<<" ";
    //cout<<endl;
    for (int i=1;i<n;i++)
    {
        int t=a[i];
        //cout<<i<<" "<<a[i]<<" "<<pre<<endl;
        if (pre==-1) 
        {
            pre=t;
            if (t==1) pre=-1,ans+=2;
            continue;
        }
        if (pre==0)
        {   
            if (a[i]==0) ans+=1;
            if (a[i]==1) ans+=3,pre=-1;
            if (a[i]==2) ans+=2,pre=-1;
        }
        else if (pre==1)
        {
            ans+=2;
            pre=-1;
        }
        else if (pre==2)
        {
            if (a[i]==0||a[i]==1) ans+=2,pre=-1;
            else pre=2; 
        }
        //cout<<i<<" "<<ans<<endl;
    }
    if (pre==0) ans++;
    if (pre==1) ans+=2;
    //cout<<pre<<endl;
    cout<<ans<<endl;
}   

D1-Seating Arrangements (easy version)

题意

        给出一个n和m,在这个题目n=1,是简单版本。然后一共有n*m个座位,每个人都有一个视力值,视力小的坐前面,然后问不方便值是多少,不方便值就是越过有人坐的位置,每个人进入座位的顺序都是按照读入的顺序。

思路

        当n=1,m最大也才300,那么我们可以按照一边读入一边来算前面有多少,直接暴力两层循环搞定,重点是在第二题D2

代码

inline void Case_Test()
{
    cin>>n>>m;
    ans=0;
    for (int i=1;i<=n*m;i++)
    {
        cin>>a[i];
        for (int j=1;j<i;j++)
            if (a[i]>a[j]) ans++;
    }
    cout<<ans<<endl;
}

D2-Seating Arrangements (hard version)

题意

        题意就是如D2,具体就是看原题吧

思路

        按照第一题的思路,我们来想第二题的暴力思路,然后想办法优化降时间复杂度,这里直接讲吧。

        因为要求最小,所以按照模拟,每个进入顺序都会进入到一个固定的位置,假如有相同的,那么先进入的会进入到后面的座位,因为如果相反,那么下一个相同的数会跨过ta,ans++。所以我们用结构体,然后sort一遍(手写cmp)得到每个人应该到的位置,最后循环来进一步判断。

struct node
{
    int v,pos;
}b[N];
int n,m,t,x,y;
struct carry
{
    int v,x,y;
}a[N];
int ans,tmp;
vector<int> v[301];
inline bool cmp(node x,node y)
{
    if (x.v==y.v) return x.pos<y.pos;
    return x.v<y.v;
}

        暴力:一个二维数组a[i][j]来表示位置,每进入一个人就填入它,然后循环模拟前面有多少个,这样的时间复杂度是O(n^3),不太现实,所以我们来降一个时间复杂度。因为每一行都是升序的,所以我们就用二分来看,但是如果是数组的话没进入就是0的话就不太行,所以换一种容器,我第一个想到的是set,set可以插入,插入后是有序的,但是我不知道可以这样...

set<int> s[n];

         我后面想的是vector容器,但是插入的话又不能有序插入,所以...我上网搜了,可以这样:

v[x].insert(lower_bound(v[x].begin() , v[x].end() , a[i].v) , a[i].v);

        这样我们就能模拟每一个v[x](x代表x排/行)中的几个人,视力是多少,当每次进入一个人的时候,就先提取出它应该在哪个x行,然后用二分(O(logn))判断在哪一个位置(前面有多少个) ,所以一共时间复杂度是O(Tn^2logn)

for (int i=1;i<=n*m;i++)
{
    x=a[i].x;
    tmp=lower_bound(v[x].begin() , v[x].end() , a[i].v)-v[x].begin();
    ans+=tmp;
    v[x].insert(lower_bound(v[x].begin() , v[x].end() , a[i].v) , a[i].v);
    //cout<<i<<" "<<tmp<<endl;
}

代码

#include<stack>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<deque>
#include<vector>
#include<iostream>
#include<map>
#include<unordered_map>
#include<set>
#include<iomanip>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define endl "\n"
#define debug(a) cout<<#a<<"="<<a<<endl;
#define eps 1e-8
using namespace std;
ll GCD(ll a,ll b){while(b^=a^=b^=a%=b);return a;}
const int inf=0x3f3f3f3f;
const int N = 90007;
struct node
{
    int v,pos;
}b[N];
int n,m,t,x,y;
struct carry
{
    int v,x,y;
}a[N];
int ans,tmp;
vector<int> v[301];
inline bool cmp(node x,node y)
{
    if (x.v==y.v) return x.pos<y.pos;
    return x.v<y.v;
}
inline void Case_Test()
{
    cin>>n>>m;
    for (int i=1;i<=n*m;i++)
    {
        cin>>a[i].v;
        b[i].v=a[i].v;
        b[i].pos=i;
    }
    sort(b+1,b+1+n*m,cmp);
    x=n;y=m;
    for (int i=n*m;i>=1;i--)
    {
        t=b[i].pos;
        a[t].x=x;
        a[t].y=y;
        y--;
        if (y==0) y=m,x--;
    }
    //for (int i=1;i<=n*m;i++) cout<<a[i].v<<" "<<a[i].x<<" "<<a[i].y<<endl;
    ans=0;
    for (int i=1;i<=300;i++) v[i].clear();
    for (int i=1;i<=n*m;i++)
    {
        x=a[i].x;
        tmp=lower_bound(v[x].begin() , v[x].end() , a[i].v)-v[x].begin();
        ans+=tmp;
        v[x].insert(lower_bound(v[x].begin() , v[x].end() , a[i].v) , a[i].v);
        //cout<<i<<" "<<tmp<<endl;
    }
    cout<<ans<<endl;
}

signed main() 
{
    #ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
            clock_t start, end;
            start = clock();
    #endif
    IOS
    int _=1;
    cin>>_;
    while (_--)
    {
        Case_Test();
    }

    #ifndef ONLINE_JUDGE
        end = clock();
        cout << endl << "Runtime: " << (double)(end - start) / CLOCKS_PER_SEC << "s\n";
    #endif
}

E-Buds Re-hanging

题意

        给出一个树,你可以将芽连上别的顶点(这个顶点不能是自己这一支,实际上就是保证还是一棵树),求问经过若干个操作后最少的叶子树是多少。

思路

        曹佬:dfs找芽,叶,其他。找到芽之后直接把芽断开。答案是芽上的叶子+其他叶子-芽数。注意特判根

代码

#include<stack>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<deque>
#include<vector>
#include<iostream>
#include<map>
#include<unordered_map>
#include<set>
#include<iomanip>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define endl "\n"
#define debug(a) cout<<#a<<"="<<a<<endl;
#define eps 1e-8
using namespace std;
ll GCD(ll a,ll b){while(b^=a^=b^=a%=b);return a;}
const int inf=0x3f3f3f3f;
const int N = 2e5+1;
struct node
{
    int to,next;
}edge[N<<1|1];
int head[N],cnt;
inline void add(int x,int to)
{
    edge[++cnt].to=to;
    edge[cnt].next=head[x];
    head[x]=cnt;
}
int u,v,n,num_ya,num_ye,ans;
inline int dfs(int x,int pre)
{
    bool ya=true;
    int cnt=0;
    for (int i=head[x];i;i=edge[i].next)
    {
        int y=edge[i].to;
        if (y==pre) continue;
        int t=dfs(y,x);
        //t=0 wu t=1 ye t=2 ya
        if (t==0) ya=false;
        if (t!=2) cnt++;
        //cout<<cnt<<" "<<num_ya<<" "<<num_ye<<" "<<ans<<endl;
    }
    //cout<<cnt<<" "<<num_ya<<" "<<num_ye<<" "<<ans<<endl;
    if (cnt&&ya&&x!=1) 
    {
        num_ya++;
        ans+=cnt;
        return 2;
    }
    //ya
    if (cnt==0&&x!=1) return 1;
    //ye
    if (x==1&&cnt==0) cnt++;
    //cout<<cnt<<" "<<num_ya<<" "<<num_ye<<" "<<ans<<endl;
    num_ye+=cnt;
    return 0;
    //wu
}
inline void Case_Test()
{
    cin>>n;
    memset(head,0,sizeof(head));
    for (int i=1;i<n;i++)
    {
        cin>>u>>v;
        add(u,v);add(v,u);
    }
    ans=0;num_ya=0;num_ye=0;cnt=0;
    dfs(1,-1);
    cout<<max(1,ans-num_ya+num_ye)<<endl;
}

signed main() 
{
    #ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
            clock_t start, end;
            start = clock();
    #endif
    IOS
    int _=1;
    cin>>_;
    while (_--)
    {
        Case_Test();
    }

    #ifndef ONLINE_JUDGE
        end = clock();
        cout << endl << "Runtime: " << (double)(end - start) / CLOCKS_PER_SEC << "s\n";
    #endif
}

F-Points Movement

        待补

总结

        这一场上了大分+178,马上1500了,小号也1404,冲冲冲!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值