Codeforces Round #509 (Div. 2) 题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tawn0000/article/details/82751588

题目传送门

A. Heist

水题,扫一遍然后记录最大值和最小值,ans = max-min+1-n;

#include <bits/stdc++.h>

using namespace std;

int main()
{
   int n;
   scanf("%d",&n);
   int maxa = 0,mina = 0x3f3f3f3f;
   for(int i = 0; i < n; i++)
   {
       int x;
       scanf("%d",&x);
       mina = min(mina,x);
       maxa = max(maxa,x);
   }
   printf("%d\n",maxa-mina-n+1);
   return 0;
}

B. Buying a TV Set

水题,先用gcd将x/y化成最简分式,然后 ans = min(a/x,b/y);注意用long long!

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;

LL gcd(LL a, LL b)
{
    return b == 0 ? a : gcd(b,a%b);
}

int main()
{
LL a,b,x,y;
 scanf("%lld%lld%lld%lld",&a,&b,&x,&y);
 LL e = gcd(x,y);
 x = x/e;
 y = y/e;
 //cout << e <<endl;
 //cout << a/x << " " << b/y <<endl;
 LL ans = min(a/x,b/y);
 printf("%lld\n",ans);
   return 0;
}

C. Coffee Break 贪心, 一个个放,当满足a[j] - a[i] -1 >= d 时可以放到a[i] 后,i 从小到大,

首先用set + lower_bound 做,nlgn 的算法,结果TLE了~

然后发现正解是用queue 直接模拟,o(n)就好了,每次都和队首元素进行比较,如果满足条件,它所在的天和队首元素一样,它入队,队首元素出队;否则,他所在的天=++count,count为当前已经记录的天数,它入队。ans = count.

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
typedef pair <LL,int> P;
int n,m,d;
int a[maxn],b[maxn];
map <int,int> mp;
int main()
{
  scanf("%d%d%d",&n,&m,&d);
  for(int i = 0; i < n; i++)
    {
      scanf("%d",&a[i]);
      b[i] = a[i];
    }
  sort(a,a+n);
  queue<int> que;
  que.push(a[0]);
  mp[a[0]] = 1;
  int j = 1;
  for(int i = 1; i < n; i++)
  {
    if(a[i]-que.front()-1 >= d) {que.push(a[i]);mp[a[i]] = mp[que.front()];que.pop();}
    else {que.push(a[i]);mp[a[i]] = ++j;}
  }
  printf("%d\n",j);
  for(int i = 0; i < n; i++)
    printf("%d%c",mp[b[i]], i == n-1 ? '\n' : ' ');
  return 0;
}

D. Glider

分别维护一下有风的前缀和和没风的前缀和,然后对于每个有风的点对前缀和用lower_bound;

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 2e5+100;
vector<int> g[maxn];
map<int,int> mp;
int a[maxn];
int b[maxn];
LL sa[maxn];
LL sb[maxn];
int main()
{
    int n,h;
    scanf("%d%d",&n,&h);
    int x = 0;
    a[0] = 0;
    b[0] = 0;
    sa[0] = 0;
    sb[0] = 0;
    for(int i = 1; i <= n; i++)
    {
      int l,r;
      scanf("%d%d",&l,&r);
      a[i] = r-l;
      b[i] = l-x,x = r;
      sa[i] = sa[i-1] + a[i];
      sb[i] = sb[i-1] + b[i];
      //cout << a[i] << " " << b[i] <<" " << sa[i] << " "<<sb[i] << endl;
    }
    LL ans = 0;
    for(int i = 1; i <= n; i++)
    {
      int idx = lower_bound(sb,sb+n+1,sb[i]+h) - sb - 1;
    // cout << sb[i] + h << " "<<idx <<endl;
      ans = max(ans,h+sa[idx]-sa[i-1]);
    }
    printf("%lld\n",ans);
    return 0;
}

E. Tree Reconstruction

树的的构造题,首先我们可以发现n肯定会出现在任何一组里,所以我们以n为根节点进行构造,其他出现过的节点分别作为n的一个子树,记录其他1-n-1分别出现的次数,然后可以发现i节点所在的分支,该分支上的节点数一定会等于i在输入数据中出现的次数。所以我们可以判断,如果n未出现n-1次,为NO,然后贪心,从最大出现的i的子树来开始构造,从大到小将未出现的节点放入该子树,只能放(i出现次数-1)个,如果发现一个放入的节点比i 大,则贪心失败,为NO;如果放完了所有为出现的节点,则贪心成功。

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 1000+10;
const int INF = 0x3f3f3f3f;
typedef pair <LL,int> P;
int n,m,d;
int a[maxn],b[maxn];
vector<int> g1,g2;
vector<int> g3[maxn];
int main()
{
  int n;
  scanf("%d",&n);
  memset(b,0,sizeof(b));
  for(int i = 1; i < n; i++)
    {
       int u,v;
       scanf("%d%d",&u,&v);
       b[u]++;
       b[v]++;
    }
  if(b[n] != n-1) {printf("NO\n");return 0;}
  for(int i = 1; i <= n; i++)
    if(b[i] == 0) g1.push_back(i);
    else g2.push_back(i);
  int j = g2.size()-2;
  //cout << b[3] << endl;
  for(int i = g1.size()-1; i >= 0; i--)
    {
      while(j >= 0)
       {
         if(g1[i] < g2[j] && b[g2[j]] > 1)
          {
            //cout << g2[j] << endl;
            g3[g2[j]].push_back(g1[i]);
            b[g2[j]]--;
            break;
          }
         else if(g1[i] > g2[j])  {printf("NO\n");return 0;}
        else j--;
       }
    }
 for(int i = 0; i < g2.size()-1; i++) g3[g2[i]].push_back(g2[i]);
 printf("YES\n");
 //cout << g3[3].size() << endl;
 for(int i = 0; i < g2.size()-1; i++)
 {
    int k = n;
    for(int j = 0; j < g3[g2[i]].size(); j++)
     {
       printf("%d %d\n",k, g3[g2[i]][j]);
       k = g3[g2[i]][j];
     }
 }
 return 0;
}

F. Ray in the tube

赛后补题,思考了一下这道题,首先由于对称性,和y没有关系,且步长不能为1,然后发现:

步长为2的会覆盖步长为6,10,14,18。。。。的情况,

步长为4的会覆盖步长为12,20,28,36。。。的情况,

然后我就无法思考了,而且对每个步长还好平移(步长-1)次,然后就不会了gg

其实我已经快做出来了,还是太菜~ 看题解后

我们可以发现所以所有数都可以表示为2^{i}2^{i} \times (2p+1) (i,p 为整数),然后诚如我所发现的,2^{}i的会覆盖掉2^{i} \times (2p+1)情况,所以我们只要枚举步长为 2^{i} (i <=  29) 就好了,然后对于每次枚举,用map记录上下点对mod = 2^{i+1} (因为i 从0开始)的余数的次数,然后不需要平移(步长-1)次,只要将上下点的余数都考虑一遍就好了。记录最大值为答案。

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair <LL,int> P;
int n,m,y;

int a[maxn];
int b[maxn];
int p[31];
map<int,int> mp1,mp2;
int main()
{
  scanf("%d%d",&n,&y);
  for(int i = 0; i < n; i++)
        scanf("%d",&a[i]);
  scanf("%d%d",&m,&y);
  for(int i = 0; i < m; i++)
      scanf("%d",&b[i]);
  p[0] = 1;
  for(int i = 1; i <= 30; i++)
      p[i] = (p[i-1] << 1);
  int ans = 2;
  for(int i = 0; i <= 29; i++)
      {
          mp1.clear();
          mp2.clear();
          int mod = p[i+1];
          for(int j = 0; j < n; j++)
              mp1[a[j]%mod]++;
          for(int j = 0; j < m; j++)
              mp2[b[j]%mod]++;
          for(int j = 0; j < n; j++)
            ans = max(ans,mp1[a[j]%mod] + mp2[(a[j] + p[i])%mod]);
          for(int j = 0; j < m; j++)
            ans = max(ans,mp2[b[j]%mod] + mp1[(b[j] + p[i])%mod]);
      }
  printf("%d\n",ans);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/Tawn0000/article/details/82751588