似乎不是很难,但似乎又很难。

A

Description

有两个数 $n,k$,在 $1\sim n$ 中选择最多的数使得不存在两个和为 $k$ 的数。

Solution

显然比 $k$ 大的数全都要选,显然 $k$ 不能选,显然比 $k$ 小的数只能选一半。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using std::cin;
using std::endl;
using std::cout;

int T, n, k;

int main()
{
    cin >> T;
    while (T--)
    {
        cin >> n >> k;
        int ans = n - k + k / 2;
        if (ans <= 0)
        {
            cout << 0 << endl;
            continue;
        }
        cout << n - k + k / 2 << endl;
        for (int i = n; i > k; i--)
            cout << i << " ";
        for (int i = k - 1; i >= (k - 1) / 2 + 1; i--)
            cout << i << " ";
        cout << endl;
    }
}

B

Description

有一个每天有 $h$ 个小时,每个小时有 $m$ 分钟的星球,给出一个时间,求下一个能在镜子中照出合法时间的时间。

Solution

只有 $0,1,2,5,8$ 组成的时间是合法的,预处理一下然后模拟。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using std::cout;
using std::cin;
using std::endl;

const int maxn = 10010;
const int number[] = {0, 1, 2, 5, 8};

bool ok[maxn];
int n, m, x[10];

inline int hash(int a, int b, int c, int d) { return a * 1000 + b * 100 + c * 10 + d; }

void init()
{
    for (int a = 0; a < 5; a++)
        for (int b = 0; b < 5; b++)
            for (int c = 0; c < 5; c++)
                for (int d = 0; d < 5; d++)
                    ok[hash(number[a], number[b], number[c], number[d])] = 1;
    x[0] = 0, x[1] = 1, x[2] = 5, x[5] = 2, x[8] = 8;
}

inline int hash2(int a, int b) { return a * 100 + b; }

inline bool check(int a, int b)
{
    if (!ok[hash2(a, b)])
        return false;
    int p = x[a % 10] * 10 + x[a / 10], q = x[b % 10] * 10 + x[b / 10];
    return p < m && q < n;
}

void work(int a, int b)
{
    while (1)
    {
        if (check(a, b))
        {
            printf("%02d:%02d\n", a, b);
            return;
        }
        b++;
        if (b == m)
            b = 0, a++;
        if (a == n)
            a = 0;
    }
}

int main()
{
    int T;
    cin >> T;
    init();
    char ch;
    while (T--)
    {
        cin >> n >> m;
        int a, b;
        cin >> a >> ch >> b;
        work(a, b);
    }
}

C

Description

有一个长度为 $n$ 只含小写字母的字符串和一个数 $k$。求一个字符串,满足这个字符串字典序比原字符串大且每个字符出现的次数都能被 $k$​​ 整除,要求满足以上要求的字典序最小的字符串。$n\leq1000$。

Solution

发现和原字符串前缀相同的位数越多字典序就可能越小,于是从大到小枚举从哪一位开始不一样,然后枚举这一位填什么字符,看剩下的位置能不能满足要求。

具体判断能不能满足要求需要看把所有字符都填到恰好被 $k$ 整除需要几个,然后看这些的和是否不大于剩下的位数。

填的时候先把能随便填的都填上 a,剩下的地方从 az 依次填就行。

建议想好再写。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using std::cout;
using std::cin;
using std::endl;

const int maxn = 1e5 + 10;

char s[maxn];

int T, n, k;
int b[30];

void output(int x, int y)
{
    s[x] = y + 'a';
    for (int i = 1; i <= x; i++)
        cout << s[i];
    int res = n - x;
    for (int i = 0; i < 26; i++)
        res -= (k - b[i] % k) % k;
    for (int i = 0; i < res; i++)
        cout << 'a';
    for (int i = 0; i < 26; i++)
    {
        b[i] %= k;
        for (int j = 0; j < (k - b[i] % k) % k; j++)
            cout << char(i + 'a');
    }
    cout << endl;
}

void solve()
{
    int sum = 0;
    for (int i = n; i >= 1; i--)
    {
        b[s[i] - 'a']--;
        for (int j = s[i] + 1 - 'a'; j < 26; j++)
        {
            b[j]++;
            sum = 0;
            for (int p = 0; p < 26; p++)
                sum += (k - b[p] % k) % k;
            if (sum <= n - i)
            {
                output(i, j);
                return;
            }
            b[j]--;
        }
    }
}

int main()
{
    std::ios::sync_with_stdio(false);
    cin >> T;
    while (T--)
    {
        memset(b, 0, sizeof(b));
        cin >> n >> k >> (s + 1);
        for (int i = 1; i <= n; i++)
            b[s[i] - 'a']++;
        int ok = 1;
        for (int i = 0; i < 26; i++)
            if (b[i] % k)
            {
                ok = 0;
                break;
            }
        if (ok)
        {
            cout << (s + 1) << endl;
            continue;
        }
        else if (n % k)
        {
            cout << -1 << endl;
            continue;
        }
        solve();
    }
}

D

Description

有一个 $n$ 个数的正整数序列,$q$ 次操作,每次把一个数乘上 $x$,问每次操作后整个序列的 gcd 对 $10^9+7$ 取模的结果。

Solution

我们只需要求出所有质因子最低次数的幂即可。

对每个质因子维护一个 multiset,乘的时候把这个数增加的质因子加进去就行。

然后把答案乘一个 $p^x$ 即可,其中 $x$​​ 是增加的幂次。

乱维护即可。。。

注意质因数分解需要做到 $O(\log n)$,为此需要预处理每个数最小质因子。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
using std::cin;
using std::cout;
using std::endl;
using std::cerr;

const int maxn = 2e5 + 10, mod = 1e9 + 7;
const int maxp = 18000;
typedef long long ll;

namespace IO
{
    const int mxsiz = 1 << 20;
    char inbuf[mxsiz], *p1, *p2;
    char outbuf[mxsiz], *op = outbuf;
    struct endio
    {
        endio(){};
        ~endio() { fwrite(outbuf, 1, op - outbuf, stdout); }
    } useless_var;
    inline char gc() { return p1 == p2 && (p2 = (p1 = inbuf) + fread(inbuf, 1, mxsiz, stdin), p1 == p2) ? EOF : *p1++; }
#define isdigit(x) (x >= '0' && x <= '9')
    inline int read()
    {
        int x = 0, f = 1;
        char ch = gc();
        for (; !isdigit(ch); ch = gc())
            if (ch == '-')
                f = -1;
        for (; isdigit(ch); ch = gc())
            x = x * 10 + ch - '0';
        return x * f;
    }
    template <typename T>
    inline void read(T &x)
    {
        x = 0;
        int f = 1;
        char ch = gc();
        for (; !isdigit(ch); ch = gc())
            if (ch == '-')
                f = -1;
        for (; isdigit(ch); ch = gc())
            x = x * 10 + ch - '0';
        x *= f;
    }
#undef isdigit
    inline bool ischar(char x)
    {
        return x >= 'A' && x <= 'z';
    }
    inline char readchar()
    {
        char ch = gc();
        while (!ischar(ch))
            ch = gc();
        return ch;
    }
    inline void push(char ch)
    {
        if (op - outbuf == mxsiz)
            fwrite(outbuf, 1, mxsiz, stdout), op = outbuf;
        *op++ = ch;
    }
    template <typename T>
    inline void work_wt(T x)
    {
        if (x > 9)
            work_wt(x / 10);
        push(x % 10 + '0');
    }
    template <typename T>
    inline void write(T x)
    {
        if (x < 0)
            x = -x, push('-');
        work_wt(x);
    }
    inline void writestr(char *s)
    {
        int n = strlen(s);
        for (int i = 0; i < n; i++)
            push(s[i]);
    }
    inline void endln() { push('\n'); }
    inline void space() { push(' '); }
    template <typename T>
    inline void writeln(T x) { write(x), endln(); }
}
using namespace IO;

int vis[maxn], p[maxp], tot, n, mi[maxn], q;
std::multiset<int> S[maxp];
std::map<int, int> mp[maxn], P;
ll ans = 1;

void init(int n)
{
    mi[1] = 2;
    for (int i = 2; i <= n; i++)
    {
        if (!vis[i])
            p[++tot] = i, P[i] = tot, mi[i] = i;
        for (int j = 1; j <= tot && p[j] * i <= n; j++)
        {
            vis[i * p[j]] = 1, mi[i * p[j]] = p[j];
            if (i % p[j] == 0)
                break;
        }
    }
}

ll qpow(ll a, int x)
{
    ll res = 1;
    for (; x; x >>= 1, a = a * a % mod)
        if (x & 1)
            res = res * a % mod;
    return res;
}

void modify(int x, int pos)
{
    if (x == 1)
        return;
    while (x > 1)
    {
        int i;
        i = P[mi[x]];
        int cnt = 0;
        while (x % p[i] == 0)
            x /= p[i], cnt++;
        if (cnt == 0)
            continue;        
        auto lst = *S[i].begin();
        int flag = S[i].size() == n - 1;
        if (mp[pos][i] != 0)
            S[i].erase(S[i].find(mp[pos][i])), S[i].insert(cnt + mp[pos][i]);
        else
            S[i].insert(cnt);
        mp[pos][i] += cnt;
        if (S[i].size() < n)
            continue;
        else
            ans = ans * qpow(p[i], flag ? *S[i].begin() : *S[i].begin() - lst) % mod;
    }
}

signed main()
{
    init(2e5);
    n = read(), q = read();
    int x;
    for (int i = 1; i <= n; i++)
    {
        x = read();
        modify(x, i);
    }
    while (q--)
    {
        int x, i;
        i = read(), x = read();
        modify(x, i);
        writeln(ans);
    }
}

E

Description

给定两个位数为 $n$ 的二进制数 $a,b$。$g(x,y)$ 为 $[x,y]$ 所有数异或起来的结果,$f(l,r)$ 为所有满足 $l\leq x\leq y\leq r$ 中 $g(x,y)$ 的最大值,求 $f(a,b)$。$n\leq10^6$​。

Solution

不会?打表!

发现当 $a,b$​ 位数不同时,答案是 $2^n-1$​。

否则当 $a=b$ 或 $b=a+1$ 或 $b$ 是奇数时,答案是 $b$。

否则答案是 $b+1$。

证明?不会!

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using std::cin;
using std::endl;
using std::cout;
using std::cerr;

std::string a, b;

std::string add(std::string x)
{
    int n = x.size();
    std::string res = x;
    int pos = n - 1;
    for (; pos >= 0; pos--)
        if (res[pos] == '1')
        {
            res[pos] = '0';
        }
        else
            break;
    res[pos] = '1';
    return res;
}

int main()
{
    int n;
    cin >> n >> a >> b;
    if (a[0] != b[0])
    {
        for (int i = 0; i < n; i++)
            cout << 1;
        cout << endl;
        return 0;
    }
    if (a == b || add(a) == b || b.back() == '1')
        cout << b << endl;
    else
        cout << add(b) << endl;
}

F

Description

这是一道交互题

有一个 $n\times m$ 的矩阵,里面是正整数。($n,m$ 给出)。

你可以询问两个长为 $w$,宽为 $h$ 的矩形是否完全相等。要求这两个矩形不能重叠也不能越界。

你需要求出所有 $(x,y)$ 的数目,满足整个矩形能用 $(1,1)-(x,y)$ 的子矩阵完全平铺出来。

询问次数最多 $3\times\log(n+m)$。

Solution

很显然可以对行和列分别求解,然后把答案乘起来。

实际上,我们只需要求出最小循环节——枚举 $n$​ 的质因子,然后试一下它的最小质因子行不行,如果可以就除以这个最小质因子然后接着算直到 $n=1$。算完最小循环节之后就能知道最多能分成几段,那么这个段数的所有因数都是合法的。

也就是说我们需要在 $3$ 次询问以内判断一个串是否能被分成完全相同的若干个串。

我们把每个完全相同的串看成一个字符,问题就转化为询问一个字符串所有字符是否都相等。

设这个字符串长度为 $L$,当 $L=2$,直接问就行。

否则串长度一定为奇数(因为是质数)我们询问 $([1,\left\lfloor\dfrac{L}{2}\right\rfloor],[\left\lceil\dfrac{L}{2}\right\rceil,L-1])$,再询问 $([1,\left\lfloor\dfrac{L}{2}\right\rfloor],[\left\lceil\dfrac{L}{2}\right\rceil+1,L])$,如果两个都完全相等,那么这个串里的字符都相等(自己想想就懂了)。

于是就结束了。

Code

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
using std::cin;
using std::cout;
using std::endl;

const int maxn = 1010;

int n, m;
int p[maxn * 2], vis[maxn * 2], tot, mi[maxn * 2];
int ansn, ansm;

void init(int n = 2000)
{
    mi[1] = 2;
    for (int i = 2; i <= n; i++)
    {
        if (!vis[i])
            p[++tot] = i, mi[i] = i;
        for (int j = 1; j <= tot && p[j] * i <= n; j++)
        {
            vis[i * p[j]] = 1, mi[i * p[j]] = p[j];
            if (i % p[j] == 0)
                break;
        }
    }
    std::ios::sync_with_stdio(false);
    cin >> ::n >> m;
}

bool ask(int l, int r, int len, bool d)
{
    if (d == 0)
        cout << "? " << n << " " << len << " " << 1 << " " << l << " " << 1 << " " << r << endl;
    else
        cout << "? " << len << " " << m << " " << l << " " << 1 << " " << r << " " << 1 << endl;
    int res;
    cin >> res;
    return res == 1 ? true : false;
}

bool queryn(int L, int len)
{
    if (L == 2)
        return ask(1, len + 1, len, 1);
    int mid = L / 2 * len;
    return ask(1, mid + 1, mid, 1) && ask(1, mid + 1 + len, mid, 1);
}

bool querym(int L, int len)
{
    if (L == 2)
        return ask(1, len + 1, len, 0);
    int mid = L / 2 * len;
    return ask(1, mid + 1, mid, 0) && ask(1, mid + 1 + len, mid, 0);
}

int work1()
{
    int res = n;
    for (int i = n; i > 1; i /= mi[i])
        if (queryn(mi[i], res / mi[i]))
            res /= mi[i];
    int times = n / res;
    int ans = 0;
    for (int i = 1; i <= times; i++)
        if (times % i == 0)
            ans++;
    return ans;
}

int work2()
{
    int res = m;
    for (int i = m; i > 1; i /= mi[i])
        if (querym(mi[i], res / mi[i]))
            res /= mi[i];
    int times = m / res;
    int ans = 0;
    for (int i = 1; i <= times; i++)
        if (times % i == 0)
            ans++;
    return ans;
}

int main()
{
    init();
    int ans = work1() * work2();
    cout << "! " << ans << endl;
}