【Codeforces - Mail.Ru Cup 2018 Round 3 】A.B.C.D.E

前言

又是一个深夜场,本来想给biubiubiu_上个紫,没想到全程不在状态,最后四题,E甚至都没读题,赛后读题+写题直接半小时就过了,开场又是写错一个for循环的变量,导致7分钟才过掉a,之后看b感觉是一个好难的题,没什么思路,画个图想了一个做法,tle,打开代码一看一个O(1e9)的for循环摆在那里,赶紧优化掉,0:22过掉之后D过的比C多,想了一个nlogn的贪心然后wa了,反向贪心又wa了,仔细思考一下,01:31一个sort就过了,回头看C,**交互模拟题,不知道为什么比D过的少,直接写,写完交,返回未知错误,我以为是哪里不合法,一直找问题,最后加了个’\n’,就过了?2:28过掉C,宣告GG。赛后看D,sdHash题,分析一下复杂度,就是个nlogn,写完之后被卡ull的单hash,向pls学习了一波多hash的模板之后过掉了。
biubiubiu_ rating-=27 1888->1861


A. Determine Line

题意

给你n个序列,每个序列中的数在该序列中只出现一次,
输出在n个序列中都出现过的数
2 &lt; = n &lt; = 100 2&lt;=n&lt;=100 2<=n<=100,每个数的值域为1-100

做法

先统计在第一个序列中出现过的,之后只保存所有之前出现过的就可以了。
我这个做法不是很好,我们其实只要统计哪些数出现过n次就可以了。

代码

#include<stdio.h>
int vis[105];
int vis2[105];
int main()
{
    int n,x,y;
    scanf("%d",&n);
    scanf("%d",&x);
    while(x--)
    {
        scanf("%d",&y);
        vis[y]=1;
    }
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d",&x);
        for(int j=1;j<=100;j++) vis2[j]=0;
        while(x--)
        {
            scanf("%d",&y);
            vis2[y]=1;
        }
        for(int j=1;j<=100;j++)
        {
            if(vis[j]&&vis2[j]) vis[j]=1;
            else vis[j]=0;
        }
    }
    for(int i=1;i<=100;i++) if(vis[i]) printf("%d ",i);
    printf("\n");
    return 0;
}


B. Divide Candies

题意

给你一个n*n的方格,点(i,j)的权值为 i 2 + j 2 i^2+j^2 i2+j2
问这个方格内有多少个数是m的倍数
1 &lt; = n &lt; = 1 0 9 , 1 &lt; = m &lt; = 1000 1&lt;=n&lt;=10^9,1&lt;=m&lt;=1000 1<=n<=109,1<=m<=1000

做法

由 于 m 的 数 据 范 围 很 小 , 我 们 可 以 直 接 算 出 m ∗ m 之 内 的 是 m 的 倍 数 的 个 数 由于m的数据范围很小,我们可以直接算出m*m之内的是m的倍数的个数 mmmm
如 果 把 m ∗ m 这 个 矩 形 内 的 所 有 点 的 横 坐 标 + m , m 的 倍 数 的 个 数 还 是 不 变 的 如果把m*m这个矩形内的所有点的横坐标+m,m的倍数的个数还是不变的 mm+mm
纵 坐 标 同 理 , 于 是 我 们 算 出 n ∗ n 中 包 含 多 少 个 m ∗ m , 之 后 同 理 再 算 剩 下 的 小 块 纵坐标同理,于是我们算出n*n中包含多少个m*m,之后同理再算剩下的小块 nnmm,
复 杂 度 O ( n l o g n ) 复杂度O(nlogn) O(nlogn)
在这里插入图片描述
如上图蓝色阴影用蓝色块翻倍算,红色阴影用红色块翻倍算,紫色块自己算
代码

#include<stdio.h>
typedef long long ll;
int main()
{
    ll n,m;
    scanf("%lld%lld",&n,&m);
    ll cnt=0;
    for(ll i=1;i<=m;i++)
    {
        for(ll j=1;j<=m;j++)
        {
            ll tmp=i*i+j*j;
            if(tmp%m==0) cnt++;
        }
    }
    ll ans=((n/m)*(n/m))*cnt;
    cnt=0;
    for(ll i=(n-n%m)+1;i<=n;i++)
    {
        for(ll j=1;j<=m;j++)
        {
            ll tmp=i*i+j*j;
            if(tmp%m==0) cnt++;
        }
    }
    ans+=2LL*cnt*(n/m);
    cnt=0;
    for(ll i=(n-n%m)+1;i<=n;i++)
    {
        for(ll j=(n-n%m)+1;j<=n;j++)
        {
            ll tmp=i*i+j*j;
            if(tmp%m==0) cnt++;
        }
    }
    ans+=cnt;
    printf("%lld\n",ans);
    return 0;
}


C. Pick Heroes

题意

有两个势力,给你2*n个英雄,每个英雄有战斗力,两方轮流选择英雄
有m对英雄有捆绑关系,对于每对捆绑的英雄,若某一个被其中一方选择
则下个回合另一方必须选择捆绑的另一个英雄
要求尽量使所选英雄总战斗力值最大
如果最开始输入1,则表示你先选择,否则对手先选择

做法

首先如果我们先手,我们肯定先选完所有捆绑英雄中最战斗力大的那一个,
之后剩下的英雄中不断选择战斗力最高的就可以
如果我们后手,对手选择捆绑英雄的情况我们只能跟着选
否则我们直接转守为攻,选择完所有捆绑英雄,
之后再每次可以选的时候直接选剩下的英雄里战斗力最大的

代码

#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 1e4+5;
int vis2[maxn];
int vis[maxn];
int l[maxn],r[maxn];
struct data
{
    int id,num;
}a[maxn];
bool cmp(const data &a,const data &b)
{
    return a.num<b.num;
}
int xx[maxn];
int main()
{
    int n,m,x;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=2*n;i++)
    {
        scanf("%d",&a[i].num);
        a[i].id=i;
        xx[i]=a[i].num;
    }
    sort(a+1,a+1+2*n,cmp);
    for(int i=1;i<=m;i++) scanf("%d%d",&l[i],&r[i]);
    int op;
    scanf("%d",&op);
    if(op==1)
    {
        int pos=1;
        for(int i=1;i<=n;i++)
        {
            if(pos<=m)
            {
                if(xx[l[pos]]>xx[r[pos]])
                {
                    vis[l[pos]]=1;
                    printf("%d\n",l[pos]);fflush(stdout);
                }
                else
                {
                    vis[r[pos]]=1;
                    printf("%d\n",r[pos]);fflush(stdout);
                }
                pos++;
            }
            else
            {
                for(int j=2*n;j>=1;j--)
                {
                    if(!vis[a[j].id])
                    {
                        vis[a[j].id]=1;
                        printf("%d\n",a[j].id);fflush(stdout);
                        break;
                    }
                }
            }
            scanf("%d",&x);
            vis[x]=1;
        }
    }
    else
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            vis[x]=1;
            int flag=0;
            for(int j=1;j<=m;j++)
            {
                if(vis2[j]) continue;
                if(l[j]==x)
                {
                    vis2[j]=1;
                    flag=1;
                    vis[r[j]]=1;
                    printf("%d\n",r[j]);fflush(stdout);
                    break;
                }
                else if(r[j]==x)
                {
                    vis2[j]=1;
                    flag=1;
                    vis[l[j]]=1;
                    printf("%d\n",l[j]);fflush(stdout);
                    break;
                }
            }
            if(flag==0)
            {
                int ff=0;
                for(int j=1;j<=m;j++)
                {
                    if(!vis2[j])
                    {
                        ff=1;
                        vis2[j]=1;
                         if(xx[l[j]]>xx[r[j]])
                        {
                            vis[l[j]]=1;
                            printf("%d\n",l[j]);fflush(stdout);
                        }
                        else
                        {
                            vis[r[j]]=1;
                            printf("%d\n",r[j]);fflush(stdout);
                        }
                        break;
                    }
                }
                if(ff==0)
                {
                    for(int j=2*n;j>=1;j--)
                    {
                        if(!vis[a[j].id])
                        {
                            vis[a[j].id]=1;
                            printf("%d\n",a[j].id);fflush(stdout);
                            break;
                        }
                    }
                }
            }
        }
    }
    return 0;
}


D. Decorate Apple Tree

题意

给你一棵树每个叶子节点上都有一个颜色,
如果某个节点是好节点,要满足他子树内所有的叶子节点颜色不同
对 每 个 k ∈ [ 1 , n ] 对每个k\in[1,n] k[1,n]输出至少有k个点为好点最少需要多少种颜色

做法

每次选出的k个点最后只有一个点是有效的,也就是子树叶子节点最多的那个点
选n个点时一定是根,选n-1个点时一定是去掉根之后包含叶子节点最多的点
继续往下同理,而一棵树的点一共有n个,所以只要对n个节点按照包含叶子节点的个数排序
之后输出就可以了。

代码

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 1e5+5;
vector<int> G[maxn];
vector<int> ans;
int sum[maxn];
void dfs(int u)
{
    if(G[u].size()==0)
    {
        sum[u]=1;
        return ;
    }
    for(int i=0;i<G[u].size();i++)
    {
        dfs(G[u][i]);
        sum[u]+=sum[G[u][i]];
    }
    return ;
}
int main()
{
    int n,x;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&x);
        G[x].push_back(i);
    }
    dfs(1);
    sort(sum+1,sum+1+n);
    for(int i=1;i<=n;i++) printf("%d ",sum[i]);
    return 0;
}


E. Check Transcription

题意

给你一个01串s,一个字符串t,
0 可 以 映 射 成 r 0 , 1 可 以 映 射 成 r 1 0可以映射成r_0,1可以映射成r_1 0r01r1
问 有 多 少 组 r 0 , r 1 可 以 满 足 映 射 之 后 s = t 问有多少组r_0,r_1可以满足映射之后s=t r0,r1s=t
∣ s ∣ &lt; = 1 0 5 , ∣ t ∣ &lt; = 1 0 6 |s|&lt;=10^5,|t|&lt;=10^6 s<=105,t<=106

做法

我 们 设 置 s 串 中 0 的 个 数 为 n u m 0 , s 串 中 1 的 个 数 为 n u m 1 我们设置s串中0的个数为num_0,s串中1的个数为num_1 s0num0,s1num1
如 果 固 定 r 0 的 长 度 , 也 就 固 定 了 r 1 的 长 度 , 也 就 确 定 了 r 0 和 r 1 如果固定r_0的长度,也就固定了r_1的长度,也就确定了r_0和r_1 r0r1,r0r1
由 于 n u m 0 ∗ l e n ( r 0 ) + n u m 1 ∗ l e n ( r 1 ) = l e n ( t ) 由于num_0*len(r_0)+num_1*len(r_1)=len(t) num0len(r0)+num1len(r1)=len(t)
我 们 只 要 枚 举 l e n ( r 0 ) 就 可 以 我们只要枚举len(r_0)就可以 len(r0)
首 先 要 判 断 l e n 1 是 否 为 整 数 , 之 后 枚 举 s 串 用 h a s h 判 断 每 一 位 0 或 1 是 否 能 正 确 转 义 首先要判断len_1是否为整数,之后枚举s串用hash判断每一位0或1是否能正确转义 len1shash01
如 果 每 一 位 都 可 以 转 义 那 么 a n s + + , 注 意 这 里 h a s h 用 自 动 溢 出 会 被 卡 , 需 要 多 h a s h 如果每一位都可以转义那么ans++,注意这里hash用自动溢出会被卡,需要多hash ans++,hashhash
复 杂 度 分 析 ( 来 自 p l s ) 复杂度分析(来自pls) (pls)
枚 举 0 的 长 度 l e n 0 , 那 么 就 有 n / l e n 0 个 可 行 解 枚举0的长度len0,那么就有n/len0个可行解 0len0n/len0
每 个 可 行 解 要 做 n 次 h a s h 每个可行解要做n次hash nhash
对 于 l e n 0 &gt; n / 2 , 总 复 杂 度 就 是 O ( n 2 / l e n 0 ) ≈ O ( n ) 对于len0&gt;n/2,总复杂度就是O(n^2/len0) \approx O(n) len0>n/2,O(n2/len0)O(n)
代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
typedef unsigned long long ull;
const int maxn = 1e6+10;
char str[maxn],t[maxn];
template <int T>
struct StringHash
{
  std::vector<std::vector<int>> ha, pw;
  std::vector<int> M;

  explicit StringHash(const std::string& s)
      : StringHash(std::vector<int>(s.begin(), s.end())) {}

  explicit StringHash(const std::vector<int>& vec)
  {
    pw = ha =
        std::vector<std::vector<int>>(T, std::vector<int>(vec.size() + 1));
    std::vector<int> C;
    for (int i = 0; i < T; i++)
    {
      pw[i][0] = 1;
      C.push_back(rand_int());
      M.push_back(rand_int());
    }
    for (int z = 0; z < T; z++)
    {
      for (size_t i = 0; i < vec.size(); ++i)
      {
        ha[z][i + 1] = (1LL * ha[z][i] * C[z] + vec[i]) % M[z],
                  pw[z][i + 1] = 1LL * pw[z][i] * C[z] % M[z];
      }
    }
  }

  // hash value of interval [a, b)
  std::vector<int> hash_interval(int a, int b)
  {
    std::vector<int> ret(T);
    for (int z = 0; z < T; z++)
    {
      ret[z] = (ha[z][b] - 1LL * ha[z][a] * pw[z][b - a] % M[z] + M[z]) % M[z];
    }
    return ret;
  }

  static int rand_int() {
    static std::mt19937 gen((std::random_device())());
    static std::uniform_int_distribution<int> uid(1e8, 1e9);
    return uid(gen);
  }
};
int num[2];
int main()
{
    scanf("%s%s",str,t);
    StringHash<10> sh(t);
    int lens=strlen(str);
    int lent=strlen(t);
    int ans=0;
    for(int i=0;i<lens;i++)
    {
        if(str[i]=='0') num[0]++;
        else num[1]++;
    }
    for(int i=1;i*num[1]<lent;i++)
    {
        if(((lent-i*num[1])%num[0])!=0) continue;
        int len0=(lent-i*num[1])/num[0];
        int len1=i;
        vector<int> flag0,flag1;
        int f0=-1,f1=-1;
        int flag=0;
        int tmp=0;
        for(int j=0;j<lens;j++)
        {
            if(str[j]=='0')
            {
               if(f0==-1)
               {
                   f0=1;
                   flag0=sh.hash_interval(tmp,tmp+len0);
                   tmp+=len0;
               }
               else
               {
                    vector<int> tt=sh.hash_interval(tmp,tmp+len0);
                    if(tt!=flag0)
                    {
                        flag=1;
                        break;
                    }
                    else
                    {
                        tmp+=len0;
                    }
               }
            }
            else
            {
               if(f1==-1)
               {
                   f1=1;
                   flag1=sh.hash_interval(tmp,tmp+len1);
                   tmp+=len1;
               }
               else
               {
                    vector<int> tt=sh.hash_interval(tmp,tmp+len1);
                    if(tt!=flag1)
                    {
                        flag=1;
                        break;
                    }
                    else
                    {
                        tmp+=len1;
                    }
               }
            }
        }
        if(flag0!=flag1&&flag==0) ans++;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值