Educational Codeforces Round 50 (Rated for Div. 2) 全题解

比赛链接

https://codeforces.com/contest/1036

A. Function Height

题目大意

n n 个点,k次操作,每次可以使一个点+1,求最终 n n 个点的最大值。

1n,k1018

题解

答案就是

kn ⌈ k n ⌉

B. Diagonal Walking v.2

题目大意

q q 次询问,每次要从(0,0)走到 (n,m) ( n , m ) ,一个点可以走多次(包括起点和终点),一个点可以走一步到达周围的8个方向,如果是斜着走的一步就叫做“好的移动”,求 (0,0) ( 0 , 0 ) 恰好走 k k 步能不能(n,m)到达,如果不能输出-1,否则输出最多可以走多少次“好的移动”。

1q104,1n,m,k1018 1 ≤ q ≤ 10 4 , 1 ≤ n , m , k ≤ 10 18

题解

-1的情况很好判断,就是max(n,m)>k

否则,如果 n+m n + m k k 不同奇偶,必须移动不好的一步来达到同奇偶,剩下的可以全部走斜的。

如果不能恰好走到,也就是max(n,m) k k 不同奇偶,那么必须做两次不好的移动使得恰好能走到(n,m)

C. Classy Numbers

题目大意

T T 次询问,求区间[Li,Ri]中有多少个非零数位 3 ≤ 3 的数字。

1T104,1Li,Ri1018 1 ≤ T ≤ 10 4 , 1 ≤ L i , R i ≤ 10 18

题解

(伪)数位DP。对于区间 [1,R] [ 1 , R ] ,拆成 [1,a×10x1],[a×10x [ 1 , a × 10 x − 1 ] , [ a × 10 x , (10a+b)×10x11],[(10a+b)×10x1 ( 10 a + b ) × 10 x − 1 − 1 ] , [ ( 10 a + b ) × 10 x − 1 , (100a+10b+c)×10x21], ( 100 a + 10 b + c ) × 10 x − 2 − 1 ] , ⋯ ,每段分别统计答案,每段的答案是很好统计的。

D. Vasya and Arrays

题目大意

有一种操作,将序列的任意一段用它们的和代替。现有两个序列 A,B A , B ,可以执行任意次操作,问两个序列最终能否相同,若能,输出两个序列相同时最长的长度,不能输出-1。

1|A|,|B|3×105,1Ai,Bi109 1 ≤ | A | , | B | ≤ 3 × 10 5 , 1 ≤ A i , B i ≤ 10 9

题解

如果两个序列的和不同输出-1。

用两个值维护两个序列的前缀和,一个值大了就往另一个值加数,如果两个值相等答案+1。

E. Covered Points

题目大意

n n 条线段,每条线段从(ai,bi) (ci,di) ( c i , d i ) ,求至少被一条线段覆盖的整点个数。保证线段两两不在一条直线上。

1n103,106ai,bi,ci,di106 1 ≤ n ≤ 10 3 , − 10 6 ≤ a i , b i , c i , d i ≤ 10 6

题解

如果线段没有交点,那么每条线段对答案的贡献就是 gcd(|ciai|,|dibi|) gcd ( | c i − a i | , | d i − b i | )

如果没有3条线段共用一个交点,那么枚举每条线段的交点,如果是整点答案-1。

如果没有上面这些限制,我们可以开一个set,存每条线段与前面线段的交点,最后答案-set的大小。

F. Relatively Prime Powers

题目大意

一个数可以表示成 ak11ak22aknn a 1 k 1 ⋅ a 2 k 2 ⋯ a n k n ,如果 gcd(k1,k2,,kn)=1 gcd ( k 1 , k 2 , ⋯ , k n ) = 1 ,就说这个数是好数。 T T 组询问,求[2,Mi]范围内的好数个数。

1T105,1Mi1018 1 ≤ T ≤ 10 5 , 1 ≤ M i ≤ 10 18

题解

显然, [2,N] [ 2 , N ] 范围内好数个数为

N1+i=2μ(i)×(N1/i1) N − 1 + ∑ i = 2 ∞ μ ( i ) × ( ⌊ N 1 / i ⌋ − 1 )

但是如果直接这样算是 O(Tlog2N) O ( T log 2 ⁡ N )

考虑离线算法,按照询问降序排列。

对于每个询问,当 i=2 i = 2 时, n n 很容易在 O(logN) O ( log ⁡ N ) 时间内得到,而 i3 i ≥ 3 时, i=3N1/i1 ∑ i = 3 ∞ ⌊ N 1 / i − 1 ⌋ 的值不超过 1.1×106 1.1 × 10 6 ,开一个数组记录 N1/i N 1 / i ,可以对于每个询问依次减到实际值。 μ(i) μ ( i ) 可以预处理 O(logN) O ( log ⁡ N )

总的时间复杂度大概是 O(TlogN+1.1×106) O ( T log ⁡ N + 1.1 × 10 6 )

G. Sources and Sinks

题目大意

将没有入度的点称为源,将没有出度的点称为汇,一个点可以既是源又是汇。给一个 n n 个点m条边的图,保证源和汇的个数相同且都 20 ≤ 20 。现有一个算法:

  1. 找一个源和一个汇,可以相同。
  2. 连一条边从汇到源,如果这个图还有源和汇,进入1。

对于这个图,如果在1过程中,无论如何选取源和汇,最后得到的图都是强连通的,输出YES,否则输出NO

题解

设源和汇的个数都为 C C

如果一个源集合S能够到达的所有汇为 T T ,对于任意S,若 |S|0,|S|C,|S||T| | S | ≠ 0 , | S | ≠ C , | S | ≥ | T | ,那么应该输出NO,因为如果 T T 中每个点都与S中的点连边,那么 S S 中的点就不能到达非T中的汇点。

现在我们用数学归纳法证明如果对于每个 S S ,都有|S|=0,|S|=C |S|<|T| | S | < | T | ,那么应该输出YES

任取一个点 s s

  1. s一定能走到 s s (显然)。
  2. s能走到的汇的集合为 T T ,如果|T|=C,那么证明了强连通;

    • 否则,记 T T 能走到的源为S S S 能走到的汇为T,由于 |T|=|S|<|T| | T | = | S | < | T ′ | ,因此每一次 |T| | T | 的值都在变大,最终变成 |T|=C | T | = C
    • 因此只要 O(C2C) O ( C 2 C ) 的检查源的集合就可以判断了。

      代码

      A. Function Height

      #include <cstdio>
      
      long long a,b;
      
      int main()
      {
        scanf("%I64d%I64d",&a,&b);
        printf("%I64d\n",(b+a-1)/a);
        return 0;
      }
      

      B. Diagonal Walking v.2

      #include <cstdio>
      #include <algorithm>
      
      int q;
      long long n,m,k;
      
      int main()
      {
        scanf("%d",&q);
        while(q--)
          {
            scanf("%I64d%I64d%I64d",&n,&m,&k);
            if(n<m)
              {
                std::swap(n,m);
              }
            if(k<n)
              {
                puts("-1");
                continue;
              }
            if((n-m)&1)
              {
                --k;
              }
            else if((k-n)&1)
              {
                k-=2;
              }
            printf("%I64d\n",k);
          }
        return 0;
      }
      

      C. Classy Numbers

      #include <cstdio>
      
      const int pow[]={1,9,81,729};
      
      int C(int a,int b)
      {
        if(b>a)
          {
            return 0;
          }
        int res=1;
        for(int i=1; i<=b; ++i)
          {
            res=res*(a-i+1)/i;
          }
        return res;
      }
      
      int getval(long long x)
      {
        int have=3,ans=0;
        long long bit=1;
        for(int i=1; i<=18; ++i)
          {
            bit*=10;
          }
        for(int i=18; i>=0; --i)
          {
            int v=x/bit;
            x%=bit;
            bit/=10;
            if(v==0)
              {
                continue;
              }
            for(int j=0; j<have; ++j)
              {
                ans+=v*C(i,j)*pow[j];
              }
            ans+=C(i,have)*pow[have];
            --have;
            if(have<0)
              {
                break;
              }
          }
        if(have>=0)
          {
            ++ans;
          }
        return ans;
      }
      
      int t;
      long long a,b;
      
      int main()
      {
        scanf("%d",&t);
        while(t--)
          {
            scanf("%I64d%I64d",&a,&b);
            printf("%d\n",getval(b)-getval(a-1));
          }
        return 0;
      }
      

      D. Vasya and Arrays

      #include <cstdio>
      
      int read()
      {
        int x=0,f=1;
        char ch=getchar();
        while((ch<'0')||(ch>'9'))
          {
            if(ch=='-')
              {
                f=-f;
              }
            ch=getchar();
          }
        while((ch>='0')&&(ch<='9'))
          {
            x=x*10+ch-'0';
            ch=getchar();
          }
        return x*f;
      }
      
      const int maxn=300000;
      
      int n,m,a[maxn+10],b[maxn+10],ans;
      long long suma,sumb;
      
      int main()
      {
        n=read();
        for(int i=1; i<=n; ++i)
          {
            a[i]=read();
            suma+=a[i];
          }
        m=read();
        for(int i=1; i<=m; ++i)
          {
            b[i]=read();
            sumb+=b[i];
          }
        if(suma!=sumb)
          {
            puts("-1");
            return 0;
          }
        suma=sumb=0;
        int l=1,r=1;
        while((l<=n)||(r<=m))
          {
            if(suma<=sumb)
              {
                suma+=a[l++];
              }
            else
              {
                sumb+=b[r++];
              }
            if(suma==sumb)
              {
                ++ans;
              }
          }
        printf("%d\n",ans);
        return 0;
      }
      

      E. Covered Points

      #include <set>
      #include <cmath>
      #include <cstdio>
      #include <iostream>
      #include <algorithm>
      
      int read()
      {
        int x=0,f=1;
        char ch=getchar();
        while((ch<'0')||(ch>'9'))
          {
            if(ch=='-')
              {
                f=-f;
              }
            ch=getchar();
          }
        while((ch>='0')&&(ch<='9'))
          {
            x=x*10+ch-'0';
            ch=getchar();
          }
        return x*f;
      }
      
      typedef std::pair<int,int> pii;
      
      const int maxn=1000;
      const double eps=0.00000001;
      
      struct point
      {
        double x,y;
      
        point(double _x=0,double _y=0):x(_x),y(_y){}
      };
      
      struct seg
      {
        point l,r;
      
        seg(point _l=0,point _r=0):l(_l),r(_r){}
      };
      
      int n,ans;
      seg s[maxn+10];
      std::set<pii> t;
      
      point getcp(seg a,seg b)
      {
        double x1=a.l.x-a.r.x,x2=b.l.x-b.r.x,y1=a.l.y-a.r.y,y2=b.l.y-b.r.y;
        double bot=y1*x2-x1*y2;
        point ans(((b.r.y-a.r.y)*(x1*x2)+a.r.x*(y1*x2)-b.r.x*(x1*y2))/bot,
                  ((a.r.x-b.r.x)*(y1*y2)+b.r.y*(y1*x2)-a.r.y*(x1*y2))/bot);
        return ans;
      }
      
      int gcd(int a,int b)
      {
        return b?gcd(b,a%b):a;
      }
      
      int tr(double x)
      {
        if(x>=0)
          {
            return (int)(x+0.5);
          }
        else
          {
            return (int)(x-0.5);
          }
      }
      
      int main()
      {
        n=read();
        for(int i=1; i<=n; ++i)
          {
            int lx=read(),ly=read(),rx=read(),ry=read();
            s[i]=seg(point(lx,ly),point(rx,ry));
            int dx=abs(rx-lx),dy=abs(ry-ly);
            ans+=gcd(dx,dy)+1;
            t.clear();
            for(int j=1; j<i; ++j)
              {
                point cp=getcp(s[i],s[j]);
                int cpx=tr(cp.x),cpy=tr(cp.y);
                if((std::abs(cpx-cp.x)<eps)&&(std::abs(cpy-cp.y)<eps)
                   &&(cpx>=std::min(lx,rx))&&(cpx>=std::min(s[j].l.x,s[j].r.x))
                   &&(cpx<=std::max(lx,rx))&&(cpx<=std::max(s[j].l.x,s[j].r.x))
                   &&(cpy>=std::min(ly,ry))&&(cpy>=std::min(s[j].l.y,s[j].r.y))
                   &&(cpy<=std::max(ly,ry))&&(cpy<=std::max(s[j].l.y,s[j].r.y)))
                  {
                    t.insert(std::make_pair(cpx,cpy));
                  }
              }
            ans-=t.size();
          }
        printf("%d\n",ans);
        return 0;
      }
      

      F. Relatively Prime Powers

      #include <cmath>
      #include <cstdio>
      #include <algorithm>
      
      const int maxk=65;
      const int maxn=100000;
      const double eps=0.000001;
      const long long maxv=1000000000000000000ll;
      
      struct query
      {
        int id;
        long long val;
      
        bool operator <(const query &other) const
        {
          return val>other.val;
        }
      };
      
      int p[maxk+10],prime[maxk+10],mu[maxk+10],cnt,end[maxk+10],t;
      long long ans[maxn+10];
      query q[maxn+10];
      
      int getmu()
      {
        p[1]=1;
        mu[1]=1;
        for(int i=1; i<=maxk; ++i)
          {
            if(!p[i])
              {
                prime[++cnt]=i;
                mu[i]=-1;
              }
            for(int j=1; (j<=cnt)&&(i*prime[j]<=maxk); ++j)
              {
                p[i*prime[j]]=1;
                if(i%prime[j]==0)
                  {
                    mu[i*prime[j]]=0;
                    break;
                  }
                mu[i*prime[j]]=-mu[i];
              }
          }
        return 0;
      }
      
      long long spow(long long x,int b)
      {
        long long res=1;
        while(b)
          {
            if(b&1)
              {
                res=res*x;
              }
            x=x*x;
            b>>=1;
          }
        return res;
      }
      
      long long check(long long x)
      {
        end[2]=sqrt(x);
        for(int i=3; i<=maxk; ++i)
          {
            while(spow(end[i],i)>x)
              {
                --end[i];
              }
          }
        long long res=x-1;
        for(int i=2; i<=maxk; ++i)
          {
            res+=mu[i]*(end[i]-1);
          }
        return res;
      }
      
      int main()
      {
        getmu();
        for(int i=3; i<=maxk; ++i)
          {
            end[i]=(long long)(pow(maxv,1.0/i)+eps);
          }
        scanf("%d",&t);
        for(int i=1; i<=t; ++i)
          {
            scanf("%I64d",&q[i].val);
            q[i].id=i;
          }
        std::sort(q+1,q+t+1);
        for(int i=1; i<=t; ++i)
          {
            ans[q[i].id]=check(q[i].val);
          }
        for(int i=1; i<=t; ++i)
          {
            printf("%I64d\n",ans[i]);
          }
        return 0;
      }
      

      G. Sources and Sinks

      #include <cstdio>
      
      int read()
      {
        int x=0,f=1;
        char ch=getchar();
        while((ch<'0')||(ch>'9'))
          {
            if(ch=='-')
              {
                f=-f;
              }
            ch=getchar();
          }
        while((ch>='0')&&(ch<='9'))
          {
            x=x*10+ch-'0';
            ch=getchar();
          }
        return x*f;
      }
      
      const int maxn=1000000;
      const int maxk=20;
      
      int source[maxk+3],id[maxn+3],n,m,cnts,cntt;
      int ru[maxn+10],chu[maxn+10],to[maxk+3];
      int pre[maxn+10],now[maxn+10],son[maxn+10],tot;
      
      int ins(int a,int b)
      {
        pre[++tot]=now[a];
        now[a]=tot;
        son[tot]=b;
        return 0;
      }
      
      int search(int u,int st)
      {
        if(id[u])
          {
            to[st]|=1<<(id[u]-1);
          }
        int j=now[u];
        while(j)
          {
            int v=son[j];
            search(v,st);
            j=pre[j];
          }
        return 0;
      }
      
      int main()
      {
        n=read();
        m=read();
        for(int i=1; i<=m; ++i)
          {
            int a=read(),b=read();
            ins(a,b);
            ++chu[a];
            ++ru[b];
          }
        for(int i=1; i<=n; ++i)
          {
            if(!ru[i])
              {
                source[++cnts]=i;
              }
            if(!chu[i])
              {
                id[i]=++cntt;
              }
          }
        for(int i=1; i<=cnts; ++i)
          {
            search(source[i],i);
          }
        int flag=0;
        for(int sta=1; sta<(1<<cnts)-1; ++sta)
          {
            int ttot=0,scnt=0,cnt=0;
            for(int i=1; i<=cnts; ++i)
              {
                if(sta&(1<<(i-1)))
                  {
                    ++scnt;
                    ttot|=to[i];
                  }
              }
            for(int i=1; i<=cnts; ++i)
              {
                if(ttot&(1<<(i-1)))
                  {
                    ++cnt;
                  }
              }
            if(cnt<=scnt)
              {
                flag=1;
                break;
              }
          }
        puts(flag?"NO":"YES");
        return 0;
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值