• CF Educational Round 50 题解


    A - Function Height

    题意:

    在平面直角坐标系中有(2n+1)个点,分别是(P_i=(i,0),0le ile 2n)。对于每个奇数(i),你一个操作可以将(P_i)向上移动(1)个单位。要让你用若干次操作使得操作后所有三角形的面积为(k)。问你操作后的(minlimits_{1le ile n}Y_{P_i}),其中(Y_p)表示点(p)的纵坐标。

    题解:

    显然要让所有点的横坐标尽量平均。又因为(P_i)的纵坐标每增加(1),总面积就增加(1)。所以总面积就等于所有增加的高度。所以答案应为(⌈dfrac{k}{n}⌉)

    Code:

    // Code by H~$~C
    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
      long long n, k;
      cin >> n >> k;
      cout << (k + n - 1) / n << endl;
      return 0;
    }
    

    B - Diagonal Walking v.2

    题意:

    在平面直角坐标系中,定义从((x,y))走一步可以走到((x-1,y-1),(x-1,y),(x-1,y+1),(x,y-1),(x,y+1),(x+1,y-1),(x+1,y),(x+1,y+1)),即(8)联通。若从((x,y))走一步走到((x',y')),且(|x-x'|=|y-y'|=1),则称这一步为"好的"。求从((0,0))走到((n,m))(k)步,可以经过一个点多次,最多有多少步是"好的"。如果(k)步之内走不到,输出-1

    题解:

    分类讨论题。下面默认(n<m)

    (k<m),则肯定就走不到了;

    否则,贪心考虑,先走(n)步"好的",然后到达((n,n)),剩下的距离为(m-n)。接着还可以走(m-n)步"好的",到达((n,m+1))((n,m))。若直接到达了((n,m)),且当剩下还要走到步数(k'!!!mod2=1)时,只能将最后一步"好的"拆成两步。若到达了((n,m+1)),那么可以在最后一步之前一直围着((n,m))绕弯子,只有最后一步不是"好的"。

    Code:

    // Code by H~$~C
    #include <bits/stdc++.h>
    using namespace std;
    
    long long n, m, k;
    
    void solve() {
      cin >> n >> m >> k;
      if (n > m) swap(n, m);
      if (k < m) {
        cout << -1 << endl;
        return ;
      }
      if ((m - n) % 2 == 0 && (k - n) % 2 == 1)
        --n, --m, k -= 2;
      cout << k - (m - n) % 2 << endl;
    }
    
    int main() {
      int tests;
      cin >> tests;
      while (tests--) solve();
      return 0;
    }
    

    C - Classy Numbers

    题意:

    定义一个数字是"好数",当且仅当它的十进制表示下有不超过(3)个数字(1sim 9)。给定([l,r],1le l,rle 10^{18}),问有多少个(x)使得(l le x le r),且(x)是"好数"。多次查询。

    题解:

    打表。预处理出所有的"好数",这个时间复杂度是(O((18*9)^3))的。然后每一次查询直接在打好的表上二分即可。

    Code:

    // Code by H~$~C
    #include <bits/stdc++.h>
    using namespace std;
    
    vector<long long> nums;
    long long pw[19];
    void init() {
      pw[0] = 1LL;
      for (int i = 1; i < 19; ++i)
        pw[i] = pw[i - 1] * 10LL;
      nums.push_back(1e18);
      for (int i = 0; i < 18; ++i) {
        for (int a1 = 1; a1 <= 9; ++a1) {
          nums.push_back(pw[i] * a1);
        }
        for (int j = i + 1; j < 18; ++j) {
          for (int a1 = 1; a1 <= 9; ++a1) {
            for (int a2 = 1; a2 <= 9; ++a2) {
              nums.push_back(pw[i] * a1 + pw[j] * a2);
            }
          }
          for (int k = j + 1; k < 18; ++k) {
            for (int a1 = 1; a1 <= 9; ++a1) {
              for (int a2 = 1; a2 <= 9; ++a2) {
                for (int a3 = 1; a3 <= 9; ++a3) {
                  nums.push_back(pw[i] * a1 + pw[j] * a2 + pw[k] * a3);
                }
              }
            }
          }
        }
      }
      sort(nums.begin(), nums.end());
      nums.erase(unique(nums.begin(), nums.end()), nums.end());
    }
    inline int get_num(long long x) {
      return upper_bound(nums.begin(), nums.end(), x) - nums.begin();
    }
    void solve() {
      long long l, r;
      cin >> l >> r;
      cout << get_num(r) - get_num(l - 1) << endl;
    }
    
    int main() {
      init();
      int tests;
      cin >> tests;
      while (tests--) solve();
      return 0;
    }
    

    D - Vasya and Arrays

    题意:

    给定(A,B)两个数列,长度分别为(n,m),数列长度不超过(3 imes 10^5),数列中每个元素的值为([1,10^9])的整数。你需要将两个数列都分割成(k)份,使得每一份的元素和对应相等。如果可行,输出最多将序列分割成多少份,如果不可行,输出-1

    题解:

    如果(sumlimits_{i=1}^nA_i eqsumlimits_{i=1}^mB_i),则输出-1。接着用双指针,若当前数组(A)上的区间的和等于数组(B)上的区间和,则答案(+1),更新两个数组上的左区间。

    Code:

    // Code by H~$~C
    #include <bits/stdc++.h>
    using namespace std;
    
    static const int Maxn = 300005;
    
    int n, m;
    int a[Maxn], b[Maxn];
    long long suma, sumb;
    
    int main() {
      ios_base::sync_with_stdio(false);
      cin.tie(nullptr), cout.tie(nullptr);
      cin >> n;
      for (int i = 1; i <= n; ++i)
        cin >> a[i], suma += a[i];
      cin >> m;
      for (int i = 1; i <= m; ++i)
        cin >> b[i], sumb += b[i];
      if (suma != sumb) return puts("-1") & 0;
      suma = sumb = 0;
      int ans = 0;
      for (int i = 1, j = 1; i <= n; ++i) {
        suma += a[i];
        while (j <= m && sumb < suma) sumb += b[j++];
        if (suma == sumb) {
          suma = sumb = 0;
          ++ans;
        }
      }
      cout << ans << endl;
      return 0;
    }
    

    E - Covered Points

    题意:

    在平面直角坐标系上有(n)条线段,求这些线段覆盖的整点的个数。(1le nle 1000)

    题解:

    可以先把所有所有线段覆盖的整点的和统计一下,然后在减去重复的,即线段的交点出现的次数(-1)

    但线段的交点不是特别好求。

    设两条线段为(p_1=(x_1,y_1),p_2=(x_2,y_2))(p_3=(x_3,y_3),p_4=(x_4,y_4))。我们考虑先求这两条线段所在的直线的交点,再判断交点是否在这两条线段上。

    若这两条直线平行,即((p_1-p_2) imes(p_3-p_4)=0)的话,那么就肯定没有交点。

    设交点为(p_0=(x,y)),则满足((p_1-p_0) imes(p_2-p_0)=0,(p_3-p_0) imes(p_4-p_0)=0)。展开后可得出:

    [(y_1-y_2)x_0+(x_2-x_1)y_0+x_1y_2-x_2y_1=0 ]

    [(y_3-y_4)x_0+(x_4-x_3)y_0+x_3y_4-x_4y_3=0 ]

    然后解这个方程组。

    (a_1=y_1-y_2,b_1=x_2-x_1,c_1=x_1y_2-x_2y_1,a_2=y_3-y_4,b_2=x_4-x_3,c_2=x_3y_4-x_4y_3),则最后可求得:

    [x_0=(c_1b_2-c_2b_1)/(a_2b_1-a_1b_2) ]

    [y_0=(a_2c_1-a_1c_2)/(a_1b_2-a_2b_1) ]

    至于如何判断点(p_0)是否在线段(p_1,p_2)上,只需要((p_0-p_1) imes(p_0-p_2)=0),且((p_0-p_1)cdot(p_0-p_2)<0)即可。

    有一个要特别注意的是这题如果你用double写,会特别卡精度。

    Code:

    // Code by H~$~C
    #include <bits/stdc++.h>
    #define x1 x1_19260817
    #define y1 y1_19260817
    #define fi first
    #define se second
    using namespace std;
    
    static const double EPS = 1e-8;
    inline int dcmp(double x) {
      return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
    }
    static const int Maxn = 1005;
    typedef pair<double, double> point;
    point operator + (point a, point b) { return point(a.fi + b.fi, a.se + b.se); }
    point operator - (point a, point b) { return point(a.fi - b.fi, a.se - b.se); }
    inline double dot(point a, point b) { return a.fi * b.fi + a.se * b.se; }
    inline double cross(point a, point b) { return a.fi * b.se - a.se * b.fi; }
    inline bool is_number(double x) {
      bool flag = false;
      int p = int(x);
      flag |= abs(x - double(p)) <= EPS;
      flag |= abs(x - double(p + 1)) <= EPS;
    //  fprintf(stderr, "is_number(%.12lf) = %d
    ", x, flag);
      return flag;
    }
    inline void to_number(double &x) {
      int p = int(x);
      if (abs(x - double(p)) <= EPS) x = double(p);
      else if (abs(x - double(p + 1)) <= EPS) x = double(p + 1);
      else if (abs(x - double(p - 1)) <= EPS) x = double(p - 1);
    }
    inline int tonumber(double x) {
      int p = int(x);
      if (abs(x - double(p)) <= EPS) return p;
      else return p + 1;
    }
    vector<point> line_intersect(point x1, point y1, point x2, point y2) {
      vector<point> res;
      if (dcmp(cross(y1 - x1, y2 - x2)) == 0) return res;
      point a = point(x1.se - y1.se, x2.se - y2.se);
      point b = point(y1.fi - x1.fi, y2.fi - x2.fi);
      point c = point(cross(x1, y1), cross(x2, y2));
      res.push_back(point(cross(c, b) / cross(b, a), cross(c, a) / cross(a, b)));
    }
    inline bool point_on_segment(point p, point A, point B) {
      if (p == A || p == B) return true;
      point a = A - p, b = B - p;
      return dcmp(cross(a, b)) == 0 && dcmp(dot(a, b)) <= 0;
    }
    vector<point> segment_intersect(point x1, point y1, point x2, point y2) {
      vector<point> res = line_intersect(x1, y1, x2, y2);
      if (res.empty()) return res;
      to_number(res[0].fi); to_number(res[0].se);
      if (!point_on_segment(res[0], x1, y1) || !point_on_segment(res[0], x2, y2)) res.pop_back();
      return res;
    }
    
    int n;
    long long ans;
    int x1[Maxn], x2[Maxn], y1[Maxn], y2[Maxn];
    
    int main() {
      cin >> n;
      for (int i = 1; i <= n; ++i) {
        cin >> x1[i] >> y1[i] >> x2[i] >> y2[i];
      }
      for (int i = 1; i <= n; ++i) {
        ans += __gcd(abs(x1[i] - x2[i]), abs(y1[i] - y2[i])) + 1;
      }
      for (int i = 1; i <= n; ++i) {
        set<pair<int, int>> st;
        for (int j = i + 1; j <= n; ++j) {
          vector<point> p = segment_intersect(point(x1[i], y1[i]), point(x2[i], y2[i]), point(x1[j], y1[j]), point(x2[j], y2[j]));
          if (p.empty()) continue;
          point q = p[0];
          if (is_number(q.fi) && is_number(q.se)) {
            int qx = tonumber(q.fi);
            int qy = tonumber(q.se);
            if (st.find(make_pair(qx, qy)) == st.end()) {
              --ans;
              st.insert({qx, qy});
            }
          }
        }
      }
      printf("%lld
    ", ans);
      return 0;
    }
    
    

    F - Relatively Prime Powers

    题意:

    对于一个数(x),当且仅当将其分解后各个质因数的指数的最大公约数为(1)时,我们称这个数是"好的"。多组询问,每次询问([2,n])区间内有多少数是"好的"。(2le nle 10^{18})

    题解:

    观察到若数(x)不是"好的",则(x=p^k),其中(p,k)是整数。

    所以可以打表,将(10^{18})内所有(k)次方数找出来,(kge 3)。这种数最多只有(10^6 imes60)个。

    查询时直接在表上二分,再加上([2,n])以内的所有完全平方数。由于sqrt的精度较高,是可以(O(log10^6 imes60))之内找出来的。
    最后再减一下就行了。

    Code:

    // Code by H~$~C
    #pragma GCC optimize("Ofast")
    #include <bits/stdc++.h>
    using namespace std;
    
    vector<long long> nums;
    void init() {
      static const long long mx = 1e18;
      nums.clear();
      for (long long i = 2; i <= 1000000; ++i) {
        long long x = i * i;
        while (true) {
          if (mx / x < i) break;
          x *= i;
          long long xx = sqrt(x);
          if (xx * xx != x)
            nums.push_back(x);
        }
      }
      sort(nums.begin(), nums.end());
      nums.erase(unique(nums.begin(), nums.end()), nums.end());
    }
    void solve() {
      long long n;
      cin >> n;
      long long ans = n - int(sqrt(n));
      int it = upper_bound(nums.begin(), nums.end(), n) - nums.begin();
      cout << ans - it << endl;
    }
    
    int main() {
      init();
      int tests;
      cin >> tests;
      while (tests--) solve();
      return 0;
    }
    
    

    G - Sources and Sinks

    题意:

    有一张DAG,定义source为入度为零的点,sink为出度为零的点。保证source和sink的个数相等且不超过(20)。定义匹配一个source和一个sink为从sink向source连一条有向边。问是否任意一个source与sink之间的完美匹配都可以使原图变成强连通。(1le nle 10^6)

    题解:

    只要任何一个sink到达任何一个source,整个图就是强连通的。所以原问题可以转化为是否任意一个source与sink之间的完美匹配都可以使任何一个sink到达任何一个source。

    设一共有(c)个source。

    如果存在一个source的集合(S)所能到达的所有sink的集合(T),若(|S|ge |T|)(|S| e c),则很显然,只要当(T)里面的所有sink连向(S)里面的部

    分source,就可以保证从(T)里面的任何一个sink到不了(S)外面的任何一个source。此时答案就是No

    如果不存在这样的(S),那么就答案是Yes。证明如下:

    • 设当前的source集合为(S),sink集合为(T),则因为(|S|<|T|)(T)中的至少(1)个点就可以到达(S)以外的source。设(Scup T)以外所有能到的点(=S')。同理,(S')所有能到达的source的集合(T')也能到达至少(1)(S')以外的点。如此类推,最后(T)就能到达所有的source。

    这样就直接预处理出每一个source所能到达的sink的集合,再枚举source的集合检查一下就好了。

    Code:

    // Code by H~$~C
    #include <bits/stdc++.h>
    using namespace std;
    
    static const int Maxn = 1000005;
    
    int n, m, c;
    vector<int> source, sink;
    vector<int> g[Maxn], rg[Maxn];
    int sink_id[Maxn];
    int cnt[21];
    int dfs(int u) {
      if (g[u].empty()) return 1 << sink_id[u];
      int res = 0;
      for (int v: g[u]) res |= dfs(v);
      return res;
    }
    
    int main() {
      scanf("%d%d", &n, &m);
      for (int i = 1; i <= m; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        rg[v].push_back(u);
      }
      for (int i = 1; i <= n; ++i) {
        if (rg[i].empty()) source.push_back(i);
        if (g[i].empty()) {
          sink_id[i] = (int)sink.size();
          sink.push_back(i);
        }
      }
      c = (int)source.size();
      for (int u: source) cnt[u] = dfs(u);
      for (int mask = 1; mask < (1 << c) - 1; ++mask) {
        int sinks = 0;
        for (int i = 0; i < c; ++i)
          if (mask & (1 << i))
            sinks |= cnt[source[i]];
        int len_mask = __builtin_popcount(mask);
        int len_sinks = __builtin_popcount(sinks);
        if (len_mask >= len_sinks) return puts("NO") & 0;
      }
      puts("YES");
      return 0;
    }
    
    
  • 相关阅读:
    JAVA日常之三
    java将字符串存入oracle的Blob中
    java连接oracle数据库
    JAVA日常之二
    JAVA日常之一
    linux日常命令之三
    linux日常命令之二
    linux日常命令之一
    Python之路【第四十篇】:django日更
    Python之路【第三十九篇】:django日更
  • 原文地址:https://www.cnblogs.com/libra9z/p/12416717.html
Copyright © 2020-2023  润新知