Codeforces Round #736 (Div. 2)

A. Gregor and Cryptography

题目描述:

\(T\)组数据,每组数据给定一个质数\(P(5≤P≤10^9)\),求一组\(a\)\(b(2≤a<b≤P)\),使得\(P\)%\(a=P\)%\(b\)

思路:

因为\(P\)是大于\(5\)的质数,所以\(P\)一定是奇数。
考虑余数为\(1\)的情况,因为\(P\)是质数,所以一定没有\(2~P-1\)中的因数,那么就可以选\(P-1\)使得余数为1。
另一个就可以选择\(\frac{P-1}{2}\),因为\(P\)一定是奇数,所以\(P-1\)一定是偶数,又知道\(P\)%\(P-1=1\),因此\(P\)%\(\frac{P-1}{2}\)也一定等于\(1\)

Code:

#include <bits/stdc++.h>

#define int long long
#define r(x) scanf("%lld", &x)
#define p(x) printf("%lld", x)

using namespace std;

void F() {
  int p;
  r(p);
  if (p & 1)
    cout << min(p - 1, (p - 1) / 2) << " " << max(p - 1, (p - 1) / 2) << endl;
  else
    cout << 2 << " " << p / 2 << endl;
}

main() {
  int t;
  r(t);

  while (t--) F();

  return 0;
}

B. Gregor and the Pawn Game

题目描述:

在一个\(N×N(1≤N≤2×10^5)\)的棋盘,我方有一棋子在第\(N\)排,对手有一些棋子在第\(1\)排,给定第一排和第\(N\)排的摆放方式(其中\(1\)为棋子,\(0\)为空位置),每个卒当上方为空位置时可以向上,当斜左方或斜右方有地方旗子时可以吃掉该棋子。问最多有多少个我方棋子到达第\(1\)排。

思路:

其实并不要考虑\(N×N\)这么大的规模,可以直接将\(N×N\)的棋盘压缩成\(2×N\)的棋盘。
对于我方棋子有\(3\)种方式可以到达终点:

  1. 上方为\(0\)时,直接到达终点。
  2. 斜左方为\(1\)时,吃掉对方棋子并到达终点。
  3. 斜右方为\(1\)时,吃掉对方棋子并到达终点。

直接模拟即可。

Code:

#include <bits/stdc++.h>

#define int long long

#define max(a, b) a > b ? a : b
#define min(a, b) a < b ? a : b

#define r(x) scanf("%lld", &x)
#define p(x) printf("%lld\n", x)

#define repo(i, x, y) for (int i = x; i <= y; i ++ )
#define repr(i, x, y) for (int i = x; i >= y; i -- )

const int N = 2e5 + 10;

int n;

bool flag[N];

char str1[N], str2[N];

void F()
{
    r(n);
    scanf("%s%s", str1 + 1, str2 + 1);

    str1[n + 1] = '0';
    memset(flag, false, sizeof flag);
    flag[0] = 1, flag[n + 1] = 1;
    int res = 0;

    repo(i, 1, n)
    {
        if (str1[i] == '0' && str2[i] == '1') res ++ ;
        else if (str1[i] == '1' && str2[i] == '1')
        {
            if (!flag[i - 1] && str1[i - 1] == '1') flag[i - 1] = 1, res ++ ;
            else if(!flag[i + 1] && str1[i + 1] == '1') flag[i + 1] = 1, res ++ ;
        }
    }

    p(res);
}

main()
{
    int T;
    r(T);

    repr(i, T, 1) F();

    return 0;
}

C. Web of Lies

题目描述:

\(N(1≤N≤2×10^5)\)个人,\(M(0≤M≤2×10^5)\)个朋友关系,点\(i\)的权利为\(i\),当点\(i\)有朋友,且朋友的权力都比点\(i\)大时,点\(i\)会“自杀”。
给定\(3\)种操作:

  1. 将点\(x\)和点\(y\)设为朋友。(插入)
  2. 将点\(x\)和点\(y\)的朋友关系取消。(删除)
  3. 查询存活点的数量。(查询)

思路:

暴力的话肯定会超时。
那么我们考虑如何优化。
不难发现,如果\(x\)“自杀”,当且仅当权力最小的朋友比\(x\)的权力大。
明白这点后,我们就只需维护当前点所有朋友的最小值。
考虑插入和删除什么时候会对答案造成影响。
插入时:只有当\(y\)\(x\)的第一个朋友,且\(x<y\)时,\(x\)一定会“自杀”,存活人数减\(1\)
删除时:只有当\(y\)\(x\)唯一的朋友,且\(x<y\)时,\(x\)会取消“自杀”,存活人数加\(1\)
初始化:因为所有人都没有朋友,所以存活人数为\(N\)

Code:

#include <bits/stdc++.h>

#define int long long

#define max(a, b) a > b ? a : b
#define min(a, b) a < b ? a : b
#define swap(a, b) (a ^= b ^= a ^= b)

#define r(x) scanf("%lld", &x)
#define p(x) printf("%lld\n", x)

#define repo(i, x, y) for (int i = x; i <= y; i ++ )
#define repr(i, x, y) for (int i = x; i >= y; i -- )

using namespace std;

const int N = 2e5 + 10;

int n, m, q, res;

set<int> st[N];

void add(int x, int y)  
{
    if (x > y) swap(x, y);  
    if (st[x].empty()) res -- ;  
    st[x].insert(y); 
    return;
}

void remove(int x, int y)  
{
    if (x > y) swap(x, y); 
    st[x].erase(y); 
    if (st[x].empty()) res ++ ; 
    return;
}

main()
{
    r(n), r(m);
    res = n;  
    repo(i, 0, m - 1)
    {
        int x, y;
        r(x), r(y);
        add(x, y);
    }

    r(q);

    repr(i, q, 1)
    {
        int op;
        r(op);
        if (op == 1)
        {
            int u, v;
            r(u), r(v);
            add(u, v);
        }
        else if (op == 2)
        {
            int u, v;
            r(u), r(v);
            remove(u, v);
        }
        else p(res);
    }

    return 0;
}

D. Integers Have Friends

题目描述:

给定一个长为\(N(1≤N≤2×10^5)\)的序列\(a_1,a_2,...a_n(1≤a_i≤10^{18})\),任意选择一个\(m(m≥2)\)使得选择连续的一段满足\(a_imodm=a_{i+1}modm=...=a_jmodm\),求最大长度。

思路:

不难看出,要求出\(gcd\)才能满足条件,可是如果只在\(a\)序列上求\(gcd\)那么就只能求出\(m=0\)的情况。
在这里,我们提出一个命题:
如果\(gcd(|a_{i+1}-a_i|,...|a_j-a_{j-1}|)>1\)当且仅当\(a_imodm=a_{i+1}modm=...=a_jmodm\)时成立。
证明:设\(a_i=k_im+r\)\(a_{i+1}=k_{i+1}m+r\)\(b_i=|a_{i+1}-a_i|\)
那么,原式\(=gcd(b_i,...,b_j)\)
\(∵b_i=|a_{i+1}-a_i|=|k_{i+1}m+r-(k_{i+1}m+r)|=|k_{i+1}-k_i|m\)
\(∴b_i≡0(modm)\)
同理,\(b_{i+1}≡0(modm),...,b_j≡0(modm)\)
\(∴gcd(b_i,...,b_j)=m\)
\(∵m≥2\)
\(∴\)原命题成立
那么,我们只需要\(ST\)表维护差值的最大公约数即可。

Code:

#include <bits/stdc++.h>

#define int long long

#define swap(a, b) (a ^= b ^= a ^= b)

#define r(x) scanf("%lld", &x)
#define p(x) printf("%lld\n", x)

#define rep(i, x, y) for (int i = x; i <= y; i ++ )
#define dec(i, x, y) for (int i = x; i >= y; i -- )

using namespace std;

const int N = 2e5 + 10;

int n;
int a[N], b[N];
int lg[N], f[N][21];

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

int query(int x, int y) {
  if (x > y) swap(x, y);
  int k = lg[y - x + 1];
  return gcd(f[y][k], f[x + (1 << k) - 1][k]);
}

void F()
{
    r(n);
    rep(i, 1, n) r(a[i]);

    rep(i, 2, n) b[i - 1] = abs(a[i] - a[i - 1]);
    rep(i, 1, n - 1)
    {
        f[i][0] = b[i];
        rep(j, 1, 20)
            if (i - (1 << (j - 1)) >= 0) f[i][j] = gcd(f[i][j - 1], f[i - (1 << (j - 1))][j - 1]);
    }

    int res = 0, r = 1;
    rep(i, 1, n - 1)
    {
        r = max(r, i);
        while (r + 1 <= n - 1 && query(i, r + 1) > 1)
            r ++ ;
        if (r == i && b[i] == 1) continue;
        res = max(res, r - i + 1);
    }

    printf("%lld\n", res + 1);
}

main()
{
    rep(i, 2, N - 1) lg[i] = lg[i >> 1] + 1;

    int T;
    r(T);
    dec(i, T, 1) F();

    return 0;
}
posted @ 2021-08-02 15:39  Andy_LJX  阅读(65)  评论(2)    收藏  举报