【官方题解】2020牛客NOIP赛前集训营-提高组(第三场)

提高T1

对于 可以看做一个整体,其权值为 ,同时注意到每次变化后 的权值其实并未发生改变,那么对 分析:

  1. ,则 ,此时 ,即

  2. 否则 ,此时 ,等式 依然成立。

综上,经过 次变换后, 的通项公式为

使用快速幂可以做到单次询问 级别的时间复杂度。

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int getint(){
    int summ=0,f=1;char ch;
    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
    if(ch=='-')f=-1,ch=getchar();
    for(;isdigit(ch);ch=getchar()) summ=(summ<<3)+(summ<<1)+ch-48;
    return summ*f;
}
inline int ksm(int x,int mi,int mod){
    int res=1;
    while(mi){
        if(mi&1) res=res*x%mod;
        x=x*x%mod;mi>>=1;
    }
    return res;
}
signed main(){
    int A,B,C,T;
    cin>>T;
    while(T--){
        A=getint(),B=getint(),C=getint();int k=getint();
        int S=A+B+C;
        cout<<C*ksm(2,k,S)%S<<"\n";
    }
    return 0;
}

提高T2

  • 想怎么做怎么做

  • 对于每一个询问,暴力枚举,再跑枚举可以到达的点即可,复杂度

  • 建立最小生成树,将询问离线,每加入一条边,暴力对每个询问进行修改即可,复杂度

  • 另外 由于,建立最小生成树,将询问离线,如果介于这次加的边和上次加的边之间,则上次加边后连通块内颜色个数,加边合并用启发式合并即可

  • 另外的答案预处理出来,询问即可

  • 对于每一次加边,如果颜色变化,则记这条边为分割边,将所有分割边记录下来,考虑到颜色数量很少,所以分割边最多条,每次暴力计算两个分割边间的答案即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define cs const 
#define fr first 
#define se second
#define ls (now<<1)
#define rs (now<<1|1)
#define mid ((l+r)>>1)
#define mp make_pair
#define pb push_back
#define ppb pop_back
#define low(i) (i&(-i))
#define par pair<int,int>
#define cn(x) memset(x, 0, sizeof(x))
#define rep(i, x, y) for(int i=x; i<=y; ++i)
#define sep(i, x, y) for(int i=x; i>=y; --i)
#define fore(i, x) for(int i=fir[x]; i; i=nex[i])
cs int G = 3;
cs int ff = 1e6 + 15;
cs int inf = 1e18 + 1;
cs int base = 4;
cs int M = 1e9 + 7;
cs int p = 1e18 + 7;
struct Node { int u, v, w; } e[ff];
struct node { int l, r, x; } Q[ff];
bool cmp(Node x, Node y) { return x.w < y.w; }
int n, m, q, opt, c[ff], X, md, su[ff], dv[ff], top; 
int fa[ff];
set<int> a[ff];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int x, int y)
{
    x = find(x); y = find(y);
    if(x == y) return;
    int szx = a[x].size(), szy = a[y].size();
    if(szx < szy) // merge x to y
    {
        fa[x] = y;
        for(set<int>::iterator it = a[x].begin(); it!=a[x].end(); ++it)
            a[y].insert(*it);
    }
    else
    {
        fa[y] = x;
        for(set<int>::iterator it = a[y].begin(); it!=a[y].end(); ++it)
            a[x].insert(*it);
    }
}
int qry(int x, int y = 0)
{
    int tmp = 1, tt = 1;
    rep(i, 1, top)
    {
        if(e[dv[i]].w > x) break;
        tmp = e[dv[i]].w; tt = su[dv[i]];
        if(i != 1) y += (e[dv[i]].w - e[dv[i - 1]].w) * su[dv[i - 1]];
    }
    y += tt * (x - tmp + 1);
    return y;
}
void init()
{
    cin >> n >> m >> q >> X >> opt;
    if(opt) cin >> md;
    rep(i, 1, n) scanf("%lld", &c[i]);
    rep(i, 1, n) fa[i] = i, a[i].insert(c[i]);
    rep(i, 1, m)
        scanf("%lld %lld %lld", &e[i].u, &e[i].v, &e[i].w);
    sort(e + 1, e + 1 + m, cmp); su[0] = 1;
    dv[++top] = 0;
    rep(i, 1, m)
    {
        int u = e[i].u, v = e[i].v;
        merge(u, v); su[i] = a[find(X)].size();
        if(su[i] > su[i - 1]) dv[++top] = i;
    }
    int Ans = 0;
    rep(i, 1, q)
    {
        int L, R; scanf("%lld %lld", &L, &R);
        if(opt == 1)
        {
            int lw = (L ^ Ans) % md + 1;
            int rw = (R ^ Ans) % md + 1;
            if(lw > rw) swap(lw, rw);
            L = lw; R = rw;
        }
        Ans = qry(R) - qry(L - 1);
        printf("%lld\n", Ans);
    }
}
signed main()
{
    int Ts = 1;
    while(Ts--)
        init();
}

以上题解是的,这里再给出一种

将所有临界点预处理出来,维护前缀和,二分找到终点对应的边,将剩下的加上即可

//O(nlogn + qlogc)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define cs const 
#define fr first 
#define se second
#define ls (now<<1)
#define rs (now<<1|1)
#define mid ((l+r)>>1)
#define mp make_pair
#define pb push_back
#define ppb pop_back
#define low(i) (i&(-i))
#define par pair<int,int>
#define cn(x) memset(x, 0, sizeof(x))
#define rep(i, x, y) for(int i=x; i<=y; ++i)
#define sep(i, x, y) for(int i=x; i>=y; --i)
#define fore(i, x) for(int i=fir[x]; i; i=nex[i])


cs int G = 3;
cs int ff = 1e6 + 15;
cs int inf = 1e18 + 1;
cs int base = 4;
cs int M = 1e9 + 7;
cs int p = 1e18 + 7;

struct Node { int u, v, w; } e[ff];
struct node { int l, r, x; } Q[ff];
bool cmp(Node x, Node y) { return x.w < y.w; }

int n, m, q, opt, c[ff], X, md, su[ff], dv[ff], top; // when >= dv  ans + 1
int fa[ff];
set<int> a[ff];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int x, int y)
{
    x = find(x); y = find(y);
    if(x == y) return;
    int szx = a[x].size(), szy = a[y].size();
    if(szx < szy) // merge x to y
    {
        fa[x] = y;
        for(set<int>::iterator it = a[x].begin(); it!=a[x].end(); ++it)
            a[y].insert(*it);
    }
    else
    {
        fa[y] = x;
        for(set<int>::iterator it = a[y].begin(); it!=a[y].end(); ++it)
            a[x].insert(*it);
    }
}

// 1 ~ x
// 1 ~ e[dv[i]].w - 1  e[dv[i]].w ~ x  -- > va = su[dv[i]]
// 1 ~ e[dv[top]].w - 1  e[dv[top]].w ~ x  --> va = su[dv[top]]
int sq[ff];
int qry(int x, int y = 0)
{
    int tmp = 1, tt = 1;
    int l = 1, r = top, ans = top + 1;
    while(l <= r)
        if(e[dv[mid]].w > x) ans = mid, r = mid - 1;
        else l = mid + 1;
    ans --;
    if(ans != 0) tmp = e[dv[ans]].w, tt = su[dv[ans]];
    y += sq[ans] + tt * (x - tmp + 1);
    return y;
}
void init()
{
    cin >> n >> m >> q >> X >> opt;
    if(opt) cin >> md;
    rep(i, 1, n) scanf("%lld", &c[i]);
    rep(i, 1, n) fa[i] = i, a[i].insert(c[i]);
    rep(i, 1, m)
        scanf("%lld %lld %lld", &e[i].u, &e[i].v, &e[i].w);
    sort(e + 1, e + 1 + m, cmp); su[0] = 1;
    dv[++top] = 0;
    rep(i, 1, m)
    {
        int u = e[i].u, v = e[i].v;
        merge(u, v); su[i] = a[find(X)].size();
        if(su[i] > su[i - 1]) dv[++top] = i;
    }
    rep(i, 2, top) sq[i] = sq[i - 1] + (e[dv[i]].w - e[dv[i - 1]].w) * su[dv[i - 1]];
    int Ans = 0;
    rep(i, 1, q)
    {
        int L, R; scanf("%lld %lld", &L, &R);
        if(opt == 1)
        {
            int lw = (L ^ Ans) % md + 1;
            int rw = (R ^ Ans) % md + 1;
            if(lw > rw) swap(lw, rw);
            L = lw; R = rw;
        }
        Ans = qry(R) - qry(L - 1);
        printf("%lld\n", Ans);
    }
}
signed main()
{
    int Ts = 1;
    while(Ts--)
        init();
}

提高T3

subtask 0

直接上 暴力。

一种可行解是对于每一个 操作,我们对该点进行 dfsbfs ,更新其他点被更新到的最小时间,操作 就直接 memset,操作 直接看目前时间与最小时间的大小输出对应答案。

也可以对于 操作枚举前面每个修改操作,这里不多讲。

#include<bits/stdc++.h>
using namespace std;
int Read() {
    int x = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-')  f = -1; ch = getchar();}
    while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
    return x * f;
}
int first[200005], nxt[200005], to[200005], tot;
void Add(int x, int y) {nxt[++tot] = first[x]; first[x] = tot; to[tot] = y;}
int tim[100005];
void modify(int u, int f, int t) {
    if(!tim[u])  tim[u] = t;
    tim[u] = min(tim[u], t);
    for(int e = first[u]; e; e = nxt[e]) {
        int v = to[e];
        if(v == f)  continue;
        modify(v, u, t + 1);
    }
}
signed main() {
    freopen("Tree6.in", "r", stdin);
    freopen("1.ans", "w", stdout);
    int n = Read(), m = Read();
    for(int i = 1; i < n; i++) {
        int x = Read(), y = Read();
        Add(x, y); Add(y, x);
    }
    dfs(1, 0);
    for(int i = 1; i <= m; i++) {
        int opt = Read(), x = Read();
        if(opt == 1)  modify(x, 0, i);
        if(opt == 2)  memset(tim, 0, sizeof(tim));
        if(opt == 3) {
            if(tim[x] && tim[x] <= i)  puts("wrxcsd");
            else  puts("orzFsYo");
        }
    }
    return 0;
}

subtask 1

菊花图的深度为 ,所以最多在 个单位时间后所有点都会被更新到,所以特判即可。

#include<bits/stdc++.h>
using namespace std;
int Read() {
    int x = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-')  f = -1; ch = getchar();}
    while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
    return x * f;
}
int first[200005], nxt[200005], to[200005], tot = 0, vis[200005], stk[55], tp;
void Add(int x, int y) {
    nxt[++tot] = first[x];
    first[x] = tot;
    to[tot] = y;
}
signed main() {
    int n = Read(), m = Read();
    for(int i = 1; i < n; i++) {
        int x = Read(), y = Read();
        Add(x, y); Add(y, x);
    }
    for(int i = 1; i <= m; i++) {
        int opt = Read(), x = Read();
        if(opt == 1 && tp < 3)
            stk[++tp] = x;
        if(opt == 2)  tp = 0;
        if(opt == 3 && tp && tp < 3)  stk[++tp] = 0;
        if(opt == 3) {
            if(tp == 3)  puts("wrxcsd");
            else {
                if(tp == 2 && stk[1] == 1)  puts("wrxcsd");
                else  if(tp == 2 && x == 1)  puts("wrxcsd");
                else  if(tp == 2 && (stk[1] == x || stk[2] == x))  puts("wrxcsd");
                else  if(tp == 1 && stk[1] == x)  puts("wrxcsd");
                else  puts("orzFsYo");
            }
        }

    }
    return 0;
}

subtask 2

对于链的情况,由于每个点度数至多为 , 我们可以写一种基于度数的做法:在 操作时将询问的点加入队列,每个单位时间暴力更新所有点扩展到的节点,可以通过该 subtask。

subtask 3

这个子任务其实有 种做法,一种是暴力,因为树高为 ,所以在至多 时间内所有点都会被更新到,枚举前面所有的修改,到没有修改或间隔时间大于最大时间时停止,输出答案即可。

另一种解法实际也是根据树高来实现的。从第一次修改时一直到其后的 个操作按照 subtask 1 的第二种方法暴力处理,之后的操作直接输出 wrxcsd 即可。

#include<bits/stdc++.h>
#define MAX 50
using namespace std;
int Read() {
    int x = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-')  f = -1; ch = getchar();}
    while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
    return x * f;
}
int first[200005], nxt[200005], to[200005], tot = 0;
void Add(int x, int y) {
    nxt[++tot] = first[x];
    first[x] = tot;
    to[tot] = y;
}
int dep[100005], fa[100005], opt[100005], Ask[100005];
void dfs(int u, int f) {
    fa[u] = f;
    dep[u] = dep[f] + 1;
    for(int e = first[u]; e; e = nxt[e]) {
        int v = to[e];
        if(v == f)  continue;
        dfs(v, u);
    }
}
int getlca(int x, int y) {
    if(dep[x] < dep[y])  swap(x, y);
    while(dep[x] != dep[y])  x = fa[x];
    while(x != y)  x = fa[x], y = fa[y];
    return x;
}
int getdis(int x, int y) {
    return dep[x] + dep[y] - 2 * dep[getlca(x, y)];
}
signed main() {
    int n = Read(), m = Read();
    for(int i = 1; i < n; i++) {
        int x = Read(), y = Read();
        Add(x, y); Add(y, x);
    }
    dfs(1, 0);
    int flag = 0;
    for(int i = 1; i <= m; i++) {
        opt[i] = Read(), Ask[i] = Read();
        if(flag)  ++flag;
        if(opt[i] == 1)
            if(flag == 0)  ++flag;
        if(opt[i] == 2)  flag = 0;
        if(opt[i] == 3) {
            if(flag >= MAX) {
                puts("wrxcsd");
                continue ;
            }
            if(!flag) {
                puts("orzFsYo");
                continue ;
            }
            int fflag = 0;
            for(int j = i - flag + 1; j < i; j++) {
                if(opt[j] == 1)
                    if(getdis(Ask[i], Ask[j]) < i - j + 1)  fflag = 1, puts("wrxcsd");
                if(fflag)  break;
            }
            if(!fflag)  puts("orzFsYo");
        }
    }
    return 0;
}

由于该代码的正确性是建立在平均树高上的,所以前 个subtask 该代码都能以极为优秀的复杂度跑过。

subtask 3

其实上面的做法已经给了我们提示,我们对询问分块,块内的询问暴力处理,一块询问结束后暴力更新该块所产生的贡献,我用的是 ST 表在 复杂度内预处理, 求出 lca ,常数较为优秀的树剖也可过。

当然,点分树也可过,这里不详讲。

#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int getint() {
    int summ = 0, f = 1; char ch;
    for (ch = getchar(); !isdigit(ch) && ch != '-'; ch = getchar()) ;
    if (ch == '-') f = -1, ch = getchar();
    for (; isdigit(ch); ch = getchar())
        summ = (summ << 3) + (summ << 1) + ch - 48;
    return summ * f;
}
const int M = 1e6 + 5;
int n, m, etot, no, t[M], cntnow, dep[M], dfn[M], out[M], lg[M];
int st[500005], minn[1000005][25], ind;
int first[1000005], nxt[1000005], to[1000005], w[1000005], tot;
inline void Add(int x, int y) {
    nxt[++etot] = first[x];
    first[x] = etot;
    to[etot] = y;
}
void dfs(int u, int fa) {
    dfn[++ind] = u; dep[u] = dep[fa] + 1; st[u] = ind;
    for (int e = first[u]; e; e = nxt[e]) {
        int v = to[e];
        if (v == fa) continue;
        dfs(v, u); dfn[++ind] = u;
    }
}
inline int my_min(int x, int y) { return dep[x] < dep[y] ? x : y; }
void prework() {
    for (int i = 1; i <= ind; i++) minn[i][0] = dfn[i];
    for (int i = 1; i <= lg[n * 2]; i++)
        for (int j = 1; j + (1 << i) - 1 <= n * 2; j++)
            minn[j][i] = my_min(minn[j][i - 1], minn[j + (1 << (i - 1))][i - 1]);
}
int Getlca(int x, int y) {
    if (st[x] > st[y]) swap(x, y);
    int l = st[x], r = st[y], k = lg[r - l + 1];
    return my_min(minn[l][k], minn[r - (1 << k) + 1][k]);
}
inline int Getdis(int x, int y) {
    return dep[x] + dep[y] - 2 * dep[Getlca(x, y)];
}
struct node {
    int t, pos;
} p[M], now[M], pp[M];
int cntp = 0, vis[M / 2], mi[M / 2], bbj;
inline void RE() {
    memset(vis, 0, sizeof(vis));
    memset(mi, 0x7f, sizeof(mi));
    for (int i = 1; i <= cntnow; i++) p[++cntp] = now[i];
    for (int i = 1; i <= cntp; i++) mi[p[i].pos] = min(mi[p[i].pos], p[i].t);
    queue<node> q; q.push(p[1]);
    int ll = 2;
    for (int i = 1; i <= cntnow; i++) mi[p[i].pos] = min(mi[p[i].pos], p[i].t);
    while (!q.empty()) {
        node u = q.front(); q.pop();
        for (int i = first[u.pos]; i; i = nxt[i]) {
            int v = to[i];
            if (!vis[v]) {
                vis[v] = 1; mi[v] = min(mi[v], mi[u.pos] + 1);
                q.push((node){mi[v], v});
                if (mi[v] == p[ll].t) {
                    vis[p[ll].pos] = 1;
                    q.push(p[ll]), ll++;
                }
            }
        }
    }
    return;
}
void Qu(int u, int nowtime) {
    if ((!bbj) && (nowtime >= mi[u])) {
        puts("wrxcsd");
        return;
    }
    for (register int i = 1; i <= cntnow; i++) {
        if (Getdis(u, now[i].pos) <= nowtime - now[i].t) {
            puts("wrxcsd");
            return;
        }
    }
    puts("orzFsYo");
    return;
}
signed main() {
    int be = clock();
    memset(mi, 0x7f, sizeof(mi));
    lg[0] = -1;
    for (int i = 1; i <= 1000000; i++) lg[i] = lg[i / 2] + 1;
    cin >> n >> m;
    p[0].t = 1e9;
    for (int i = 1, u, v; i < n; i++) {
        u = getint(); v = getint();
        Add(u, v); Add(v, u);
    }
    dfs(1, 0); prework();
    int block = sqrt(m) * 2;
    for (int nn = 1, op, u; nn <= m; nn++) {
        if (nn % block == 0)
            RE(), cntnow = bbj = 0;
        op = getint(); u = getint();
        if (op == 1) {
            now[++cntnow] = (node){nn, u};
        }
        else if (op == 2) {
            bbj = 1;,cntnow = cntp = 0;
        }
        else
            Qu(u, nn);
    }
    return 0;
}

提高T4题解

首先勇士只会增加防,于是打每只怪的回合数是不变的。然后又因为在任何时候防都不可能大于怪物的攻,所以每时每刻都一定有伤害,所以1防对每只怪的效果是不变的。效果即是降低伤害,以下称作减伤。

可以这么考虑,最小化受到的伤害,相当于最大化减伤

定义怪物 的回合数为 ,拿到的蓝宝石数量为 ,定义 为一只怪的性价比,设为

首先考虑菊花图的情况:考虑一个最优的打怪序列 ,若交换 ,目前减伤的变化为 ,因为交换后的序列一定不更优,

于是有:

移项得:

于是只需要按性价比排序,依次打即可。

然后考虑菊花图加强版的情况:用到了以下一个结论:**如果一只怪a挡在b前面(必须打a才能打b),如果,则打完a后立即打b一定最优。**

证明:假设存在一个最优的打法为:打完a后又打了一连串的怪后才打b,根据前面的证明,所有一定大于,(否则不会在b前面打),又因为,所以所有 ,那这一连串的怪应该在a之前打会更优,矛盾,于是不存在任何怪会在打了之后打,然后打,即打之后会立即打

于是可以从叶子开始,如果此节点比父节点的性价比高,就将两个节点用并查集缩为一个节点,缩完后整棵树就成了一个以性价比为关键字的大根堆。然后将当前能达到的节点的性价比为关键字放入堆中,依次取出最大的,并更新当前能达到的节点。最终得到的序列即是打怪顺序。

然后考虑的情况:此时一只怪后面可能存在多只怪被挡住。仍然是之前的证明,可以证明如果子节点性价比比父节点更高,则打完父节点后一定就打子节点。于是有一个的朴素做法:从叶节点开始,如果a比父节点b性价比高,就将其缩为一个节点,但此时树的形态会改变,于是需要将a的所有子节点合并到b的子节点下。缩完后也会是一个大根堆,每次打怪的时候,进入一个大点之后,每个大点内部处理一下即可。

发现一个大点的内部一定是一次性打完的,于是可以整体考虑一个大点,则这个大点以外的每1防对这整个大点的减伤为,同理,打完这一个大点会加的防御。于是合并时不需要改变树的形态,只需要把子节点a的参数合并到父节点b即可,即。于是从叶子节点依次向上传导参数即可。复杂度O(nlogn)。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int Read() {
    int x = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-')  f = -1; ch = getchar();}
    while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
    return x * f;
}
int first[200005], nxt[200005], to[200005], tot = 0;
void Add(int x, int y) {nxt[++tot] = first[x]; first[x] = tot; to[tot] = y;}
int fa[100005], b[100005], a[100005], d[100005], hh[100005], val[100005], HH[100005], Val[100005], tim[100005];
int vis[100005], sc[100005];
int ffa[500005];
int findfa(int x) {return (ffa[x] == x) ? x : ffa[x] = findfa(ffa[x]);}
void fight(int x) {
    //cout << x << endl;
    b[1] -= (a[x] - d[1]) * hh[x];
    d[1] += val[x];
}
void dfs(int u, int F) {
    fa[u] = F;
    for(int e = first[u]; e; e = nxt[e]) {
        int v = to[e];
        if(v == F)  continue;
        dfs(v, u);
    }
}
vector<int> Nxt[100005];
void Do(int u) {
    fight(u); sc[u] = 1;
    for(int i = 0; i < Nxt[u].size(); i++) {
        Do(Nxt[u][i]);
    }
}
signed main() {
    priority_queue<pair<double, int> > q;
    int n; scanf("%lld", &n);
    for(int i = 1; i < n; i++) {
        int x, y;
        scanf("%lld%lld", &x, &y);
        Add(x, y); Add(y, x);
    }
    dfs(1, 0);
    scanf("%lld%lld%lld", &b[1], &a[1], &d[1]);
    for(int i = 2; i <= n; i++) {
        scanf("%lld%lld%lld%lld", &b[i], &a[i], &d[i], &val[i]);
        hh[i] = b[i] / (a[1] - d[i]);  HH[i] = hh[i]; Val[i] = val[i];
        if(b[i] % (a[1] - d[i]) == 0)  --hh[i], --HH[i];
        q.push(make_pair(1.0 * val[i] / hh[i], i));
    }
    sc[1] = 1;
    for(int i = 1; i <= n; i++)  ffa[i] = i;
    while(!q.empty()) {
        int u = q.top().second; q.pop();
        if(vis[u])  continue;  vis[u] = 1;
        if(sc[fa[u]])  {Do(u); continue;}
        HH[findfa(fa[u])] += HH[u], Val[findfa(fa[u])] += Val[u];
        Nxt[ffa[fa[u]]].push_back(u);
        ffa[u] = ffa[fa[u]];
        q.push(make_pair(1.0 * Val[ffa[fa[u]]] / HH[ffa[fa[u]]], ffa[fa[u]]));
    }
    cout << b[1] << endl;
    return 0;
}
全部评论
T1如果A,B,C都等于0好像就被HACK了……
1 回复 分享
发布于 2020-10-23 07:57
第四题为什么一定打的过啊,求助。生命值可能很小啊?
1 回复 分享
发布于 2020-10-23 18:54
只有我傻到T2打了个kruskal重构树上跑线段树合并吗/kel
1 回复 分享
发布于 2020-10-23 19:38
前排兜售面包,可乐,以及各种零食
点赞 回复 分享
发布于 2020-10-23 08:09
T1 hack: 1 0 0 1 1 标程输出0,答案是1。
点赞 回复 分享
发布于 2020-10-23 09:46
T4是不是没判打不过的情况啊
点赞 回复 分享
发布于 2020-10-23 09:52
T1证明有误,C最大可以等于S,此时2*C-S=S=C,但(2*C-S)%S = 0,此时另一个数S-C=0。因为当S-C>C时,另一个数变为S-2*C>0,所以只有初始状态C=S时才有可能会发生这种情况。换句话说,对于所有0 0 C k都该输出C,而std及其推导都给出了错误答案0
点赞 回复 分享
发布于 2020-10-23 12:04
@0o_love_o0 Hack您 A 的std。 ```cpp 1 0 0 1000 1000000000 ``` 答案是 1000 您的代码应该会输出 0
点赞 回复 分享
发布于 2020-10-23 19:05

相关推荐

Pandaileee:校友加油我现在也只有一个保底太难了
点赞 评论 收藏
分享
点赞 评论 收藏
分享
9 收藏 评论
分享
牛客网
牛客企业服务