2019年华南理⼯⼤学程序设计竞赛 (春季赛)(全题目代码)

2019年华南理⼯⼤学程序设计竞赛 (春季赛)

所有题目的链接:
https://ac.nowcoder.com/acm/contest/625#question
(题面尽量看牛客里的,本博客的题面有些乱序QAQ)题目的解法讲解会后续慢慢补上。Qrz

Problem A. NB群友
Problem B. 修仙时在做什么?有没有空?可以来炼丹吗?
Problem C. 六学家的困惑
Problem D. 神经⽹络
Problem E. 数独挑战
Problem F. 翻牌游戏
Problem G. 铁树开花
Problem H. Parco_Love_GCD
Problem I. 炒股
Problem J. 单⾝狗救星
Problem K. Parco_Love_String
Problem L. ⽯头剪⼑布

Problem A. NB 群友

Input file: standard input
Output file: standard output
CC 是著名的算法竞赛选⼿,他不仅⼈⻓得帅,⽽且技术了得,⾃然⽽然就有了许多粉丝。为了能帮助粉丝们提⾼竞技⽔平,CC 建⽴了⼀个粉丝群,每天 CC 都会在粉丝群⾥和群友深⼊交流⿊科技。然⽽,有些群友⽼是不努⼒训练,成天想着复读,⽐如当 CC 在群⾥⾯发了个整数 0,那紧接着就会有群友发整数 1,然后⼜会有群友发整数 2……这引起了 CC 的不满,于是 CC 决定踢掉⼀些群友。CC 的粉丝群⼈数为⽆穷⼤。当 CC 发出整数 0 后,其他群友就会跟着轮流发整数 1, 2, 3, 4, · · · ,依此类推。需要注意的是,每个群友都会恰好发⼀次整数,每个群友发的整数两两不同CC 认为,在不考虑前导零的情况下,如果某个群友发的整数在⼗进制表⽰下的各位数字不含 0 及 1,那么这个群友就是NB 的,否则就是不 NB 的。例如,群友 A 发的整数是 3482,该数的各位数字分别为 3、4、8、2,其中不含 0、1,因此群友 A 是 NB 的;另⼀⽅⾯,群友 B 发的整数 402,⽽该数的⼗位数字是 0,因此群友B 是不 NB 的。
现在 CC 决定,踢掉所有不 NB 的群友。于是经过⼀番奥妙重重的踢⼈操作后,粉丝群⾥只剩下 NB群友。然⽽,CC 觉得剩下的这些 NB 群友还是 too naive,因此他打算邀请⼀些 NB 群友参加线下训练营(来现场教做⼈)。具体来说,CC 会给出两个正整数 L, R,然后他会邀请发出的整数的各位数字之积在区间 [L, R] 内的 NB 群友。
举例来说,假如 CC 给出的区间为 L = 50, R = 300,那么发了 567 的群友会被邀请线下参赛,因为5 × 6 × 7 = 210;同理,发了 255 的群友也会被邀请,因为 2 × 5 × 5 = 50。但是,发了 328 的群友则不会收到邀请,因为 3 × 2 × 8 = 48 /∈ [50, 300]。
现在 CC 想知道,他究竟会邀请多少 NB 群友参加线下训练营(线下⾃闭)呢?
Input
第⼀⾏是⼀个整数 T (1 ≤ T ≤ 50),表⽰数据组数。
接下来 T ⾏,每⾏两个整数 L, R (1 ≤ L ≤ R ≤ 2
32 − 1),表⽰⼀组询问。
Output
输出共 T ⾏。对于每个询问,输出⼀⾏⼀个整数 ans,表⽰ CC 邀请参加线下训练营的 NB 群友⼈数模
109 + 7 的结果。
Example
standard input standard output
4
3 6
4 9
2147483648 4294967295
5 5
7
13
793516016
1

#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef double DB;

const int maxT = 50;
const int P = 1e9+7;
const LL MaxInt = (1LL<<32) - 1;

LL Fac[100], InvFac[100];
LL C[100][100];
int cnt[20];

LL lim;
LL dfs(LL now, int pt) {
    if(pt<=1) {
        int s = 0;
        LL ret = 1;
        REP(i,2,9) {
            s += cnt[i];
            ret = ret * C[s][cnt[i]] % P;
        }
        if(s==0) return 0LL;
        return ret;
    }
    LL ret = 0;
    cnt[pt] = 0;
    while(now <= lim) {
        ret = (ret + dfs(now, pt-1)) % P;
        now *= pt, ++cnt[pt];
    }
    return ret;
}

int main() {
    C[0][0] = 1;
    for(int i = 1; i <= 50; ++i) {
        C[i][0] = C[i][i] = 1;
        for(int k = 1; k < i; ++k) C[i][k] = (C[i-1][k] + C[i-1][k-1]) % P;
    }
    int _; scanf("%d", &_);
    while(_--) {
        LL a,b;
        scanf("%lld%lld", &a, &b);
        lim = b;
        LL sb = dfs(1,9);
        lim = a-1;
        LL sa = dfs(1,9);
        LL ans = (sb - sa + P) % P;
        printf("%lld\n", ans);
    }
    return 0;
}

回到题目列表

Problem B. 修仙时在做什么?有没有空?可以来炼丹吗?

Input file: standard input
Output file: standard output
众所周知,杨⼤佬很强(从名字就可以看出来了),但他不仅仅满⾜于此,还想要变得更强,所以他每天都在爆肝修仙打 cf。然⽽过了⼀段时间,杨⼤佬发现,他已经变得反⼿⽆⼒,正⼿不精,脚步松散,反应迟钝,肝也爆不了,仙也修不动,有时甚⾄会产⽣“1 分钟只有 59 秒”这样的错觉。为了恢复往⽇的雄⻛,杨⼤佬采取了⼀系列措施——⽐如在修仙的时候顺便炼些丹药拿来补肝,于是很快,杨⼤佬⼜变
得和过去⼀样健康了。恢复了精⼒的杨⼤佬,开始思考起了⼀个⼩问题。为了能正确的描述这个问题,让我们先来回顾⼀些背景知识。每种丹药都有⼀个⾮负整数能⼒值,杨⼤佬在炼丹时,每次可以花费⼀些仙⽓对某个丹药的能⼒值进⾏修改。具体来说,如果有个丹药的能⼒值是 x,⽽杨⼤佬想将这个丹药的能⼒值变成 x ⊕ 2i,则他需
要花费的仙⽓值 f airy(x, i) 为:f airy(x, i) = (max{x, x ⊕ 2i})2imod 19260817 + 1
其中 ⊕ 指按位异或。现在考虑两个能⼒值不同的丹药,设他们的能⼒值分别为 u 和 v。根据上⼀段关于 f airy 函数的描述不难知道,如果杨⼤佬想将能⼒值为 u 的丹药修改成能⼒值为 v 的丹药,那么他必然是按照⼀定的顺序轮流修改⾮负整数 u 的每⼀个⼆进制位,直到将其修改成 v,在这⼀过程中杨⼤佬花费的总仙⽓值就是每次修改单⼀⼆进制位所花费的仙⽓值(f airy 值)之和。显然,在将 u 修改成 v 的所有操作顺序中,应存在⼀个顺序使得杨⼤佬花费的总仙⽓最少,我们记这个最⼩仙⽓花费值为 angel(u, v)。有了 f airy(x, i) 和 angel(u, v) 的定义后,我们终于可以毫⽆偏差地描述杨⼤佬的⼩问题了。杨⼤佬⼿上有 n 个丹药,其中第 i 个丹药的能⼒值为 ai。现在,杨⼤佬想将某个丹药的能⼒值变成除它本⾝外的其他某个丹药的能⼒值。杨⼤佬想知道的是,他最少需要花费多少仙⽓才能达成这⼀⽬的,也就是说杨⼤佬想知道下⾯这个式⼦的答案:min1≤i,k≤n, i̸=k{angel(ai, ak)}你能帮杨⼤佬解决这个问题吗?
Input
第⼀⾏是⼀个整数 n (1 ≤ n ≤ 2 × 105),表⽰杨⼤佬拥有的丹药个数。接下来 n ⾏,每⾏⼀个整数 ai (0 ≤ ai ≤ 218 − 1),给出了每个丹药的能⼒值。
Output
输出⼀⾏⼀个整数 ans,表⽰ min1≤i,k≤n, i̸=k{angel(ai, ak)} 的值。
Example
standard input standard output
5
23
76
51
93
42
2071072

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 2e5;
const int maxm = 18;
const int maxV = 1<<maxm;
const int INF = 0x3f3f3f3f;
const int P = 19260817;
int cost[maxV][maxm];
int start[maxn], mark[maxV], ans = INF, cc = INF, n;

inline LL PowMod(LL a, LL b) { LL r=1; while(b) { if(b&1) r=r*a%P; a=a*a%P, b>>=1; } return r; }

void init()
{
    for (int s = 0; s < maxV; ++s) {
        for(int i = 0; i < maxm; ++i)
        {
            cost[s][i] = PowMod( max(s,s^(1<<i))%P, 1<<i ) % P + 1;
        }
    }
}
struct node
{
    int u,dis;
    node(int _u=0, int _dis = 0): u(_u), dis(_dis){}
    bool operator <(const node& nn) const
    {
        return dis > nn.dis;
    }
};
priority_queue<node>que;
int dis[maxV];
void solve(int mask)
{
    while(!que.empty()) que.pop();
    for (int i = 0; i < maxV; ++i) dis[i] = INF;
    for (int i = 0; i < n; ++i)
        if(start[i] >> mask & 1) 
        {
            dis[start[i]] = 0;
            que.push(node(start[i], 0));
        }
    while(!que.empty())
    {
        node tmp = que.top(); que.pop();
        int u = tmp.u, d = tmp.dis;
        if(d >= ans) return;
        if(d != dis[u]) continue;
        if(d && mark[u])
        {
            ans = d;
            return;
        }
        for(int i = 0; i < maxm; ++i)
            if(dis[u^(1<<i)] > d + cost[u][i])
            {
                dis[u^(1<<i)] = d + cost[u][i];
                que.push(node(u^(1<<i), dis[u^(1<<i)]));
            }
    }
}
int main()
{
    init();
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) 
    {
        scanf("%d", &start[i]);
        if(mark[start[i]]==1) exit( puts("0")*0 );
        mark[start[i]] = 1;
    }
    for (int i = 0; i < maxm; ++i) solve(i);
    printf("%d\n",ans);
    return 0;
}

回到题目列表

Problem C. 六学家的困惑

Input file: standard input
Output file: standard output
⼩六喜欢两全其美的事情,今天就正好有⼀个这样的机会。⼩六⾯前有两根管⼦,管⼦⾥⾯放满了数字为 1 到 9 的⼩球。每次取球时,⼩六会先选择⼀根管⼦,再从这根管⼦的某⼀侧(左侧或右侧)取出⼀个球。在满⾜取球规则的情况下,他可以任意顺序取出所有⼩球。假如⼩六依次取出的球的编号为 a1, a2, · · · , an,则他最后就得到了⼀个形如 a1a2 · · · an 样的⼗进制数。⼩六希望他的取球顺序所组成的数是最⼤的,你可以帮⼀下他吗?
Input
第⼀⾏输⼊数据组数 T (T ≤ 14)。
接下来 T ⾏,每⾏输⼊两串只含 1 到 9 的数字,表⽰两根管⼦⾥的⼩球的数字。每根管⼦内⾄少含 1 个⼩球,且数量不超过 40 个。
Output
对每⼀组数据,输出⼀⾏ Case #x: A ,其中 x 是数据组数编号(从 1 开始),A 是⼩六能组成的最⼤的数。
Example
standard input standard output
2
123456 123456
9876346789 9854894589
Case #1: 665544332211
Case #2: 99998888776655498443

#include<bits/stdc++.h>

using namespace std;

const int maxT = 14;
const int maxn = 40;

int cmp(char *s1, int p1, int d1, int n1, char *s2, int p2, int d2, int n2) {
    int n = min(n1,n2);
    for(int i = 1; i <= n; ++i) {
        if(s1[p1] < s2[p2]) return -1;
        if(s1[p1] > s2[p2]) return 1;
        p1+=d1, p2+=d2;
    }
    if(n1 < n2) return -1;
    if(n1 > n2) return 1;
    return 0;
}

int main() {
    int __; scanf("%d", &__);
    assert(1<=__ && __<=maxT);
    char s1[maxn+5], s2[maxn+5];
    char ans[maxn*2+5];
    for(int _ = 1; _ <= __; ++_) {
        scanf("%s%s", s1, s2);
        int l1 = 0, r1 = strlen(s1)-1;
        int l2 = 0, r2 = strlen(s2)-1;
        int n = r1+r2+2;
        for(int i = 0; i < n; ++i) {
            int di1 = 0, di2 = 0;
            if(cmp(s1, l1, 1, r1-l1+1, s1, r1, -1, r1-l1+1) >= 0) di1 = 1;
            else di1 = -1;

            if(cmp(s2, l2, 1, r2-l2+1, s2, r2, -1, r2-l2+1) >= 0) di2 = 1;
            else di2 = -1;

            int pt = 0, di = 0;
            if(cmp(s1, (di1==1)?l1:r1, di1, r1-l1+1, s2, (di2==1)?l2:r2, di2, r2-l2+1) >= 0)
                pt = 1, di = di1;
            else pt = 2, di = di2;
            char c = 0;
            if(pt==1) {
                if(di==1) ans[i] = s1[l1++];
                else ans[i] = s1[r1--];
            }
            else if(pt==2) {
                if(di==1) ans[i] = s2[l2++];
                else ans[i] = s2[r2--];
            }
        }
        ans[n] = '\0';
        printf("Case #%d: %s\n", _, ans);
    }
	return 0;
}

回到题目列表

Problem D. 神经⽹络

Input file: standard input
Output file: standard output
Nowing·钟喜欢对称的东西,但可惜世界上绝⼤部分事物都是不对称的,因此 Nowing 精神崩溃了。众所周知,Sega·⻩精通神经⽹络,因此他打算帮助 Nowing 修复他的神经。当 Nowing·钟精神正常时,他的神经可以看作是⼀棵由 n 个神经元组成的树。这 n 个神经元编号为 1 ∼ n,且恰由 n n 1 条神经纤维连通,每个神经元都有⼀个神经权重 ai。⽽当 Nowing 精神崩溃后,这 n n 1 条神经纤维全部断裂,n 个神经元变得两两互不相通。Sega·⻩的⼯作就是按某种顺序依次修复这 n n 1 条神经纤维。每当 Sega·⻩修复完⼀条神经纤维,便会有⼀些新的神经元对相连通,故会产⽣⼀些神经脉冲。假
设 Sega 准备修复⼀条连接 u, v 神经元的神经纤维,设在修复前 u 所在连通块的结点集合为 G(u),v 所在连通块的结点集合为 G(v)。那么在修复这条神经纤维后,产⽣的神经脉冲值计算公式如下:pulse(< u, v >) = ∑a∈G(u)dist(a, v) + ∑b∈G(v)dist(b, u)其中 dist(a, b) 指在原本的神经元树上,在不⾛重复神经元的情况下,由神经元 a ⾛到神经元 b 时经过的所有神经元的神经权重之和。我们来举个例⼦。考虑上图,图中每个结点上的数值就是它本⾝的神经权重,其中结点 u 的神经权重为 8,结点 v 的神经权重为 2。现在要修复图中的虚线边,则修复该边所产⽣的神经脉冲按如下过程计算:
∑a∈G(u)dist(a, v) = (6 + 8 + 2) + (7 + 8 + 2) + (8 + 2) = 43∑b∈G(v)dist(b, u) = (3 + 9 + 2 + 8) + (9 + 2 + 8) + (4 + 2 + 8) + (2 + 8) = 65pulse(< u, v >) = 43 + 65 = 108
修复过程中产⽣的总神经脉冲量,是指在修复这 n n 1 条神经纤维时,所产⽣的神经脉冲数值之和。
在这里插入图片描述
对于 Sega·⻩来说,他每次会从还没有被修复的神经纤维中随机等概率地选择⼀条进⾏修复,因此最终产⽣的总神经脉冲量往往是难以预测的。不过 Sega 并不在意这些,他在意的是,最终产⽣的总神经脉冲量的期望值是多少。不难证明这个期望值⼀定是个有理数,所以 Sega 只想知道这个期望值在模 998244353 意义下的结果,但他觉得这个问题太简单了,于是就扔给你来做。你能解决这个问题吗?
Input
第⼀⾏⼀个整数 n (1 ≤ n ≤ 105),表⽰ Nowing·钟的神经结点个数。第⼆⾏有 n 个整数,其中第 i 个整数 ai (1 ≤ ai ≤ 105) 表⽰第 i 个神经结点的神经权重。接下来是 n n 1 ⾏,每⾏两个整数 u, v (1 ≤ u, v ≤ n),表⽰有⼀条神经纤维连接编号为 u, v 的两个神经
结点。数据保证给出的神经结点以及神经纤维构成⼀棵树。
Output
输出⼀⾏⼀个整数 ans (0 ≤ ans < 998244353),表⽰产⽣的总神经脉冲量期望值在模 998244353 意义下的结果。也就是说,如果实际期望值为 pq,那么 ans 应满⾜ ans × q ≡ p (mod 998244353)。
Example
standard input standard output
4
16 13 8 9
1 2
3 1
3 4
665496476
Notes
样例的中实际的总神经脉冲量期望值为 7223 ,但因为 665496476 × 3 ≡ 722 (mod 998244353),故应输出665496476。

#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef double DB;

const int maxn = 1e5;
const int maxa = 1e5;

const LL P = ((7 * 17) << 23) + 1, gg = 3;
const int maxL = 1<<17;

inline LL PowMod(LL a, LL b) { LL r=1; while(b) { if(b&1) r=r*a%P; a=a*a%P, b>>=1; } return r; }
LL A[maxL+5], B[maxL+5];
void NTT(LL *a, int len, int type) {
    int i, j, k, l;
    for(i=1, j=len>>1; i<len-1; ++i) {
        if(i<j) swap(a[i], a[j]);
        k = len>>1;
        while(j>=k)
            j -= k, k >>= 1;
        j += k;
    }
    LL var, step, u, v;
    for(l=2; l<=len; l<<=1) {
        step = PowMod(gg, (P-1)/l);
        for(k=0; k<len; k+=l) {
            var = 1;
            for(i=k; i<k+l/2; ++i) {
                u = a[i], v = var*a[i+l/2] % P;
                a[i] = (u+v) % P;
                a[i+l/2] = (u-v+P) % P;
                var = var*step % P;
            }
        }
    }
    if(type == -1) {
        for(i=1; i<len/2; ++i) swap(a[i], a[len-i]);
        LL inv = PowMod(len, P-2);
        for(i=0; i<len; ++i) a[i] = a[i]*inv % P;
    }
}

int n, ai[maxn+5];
LL inv[maxn+5];
LL ans = 0;

vector<int> G[maxn+5];
int que[maxn+5], fa[maxn+5], sz[maxn+5], msz[maxn+5];
bool ban[maxn+5];
int FindRoot(int x) {
    int s = 1, t = 1;
    que[1] = x, fa[x] = 0;
    while(s <= t) {
        x = que[s++], sz[x] = 1, msz[x] = 0;
        for(auto v : G[x])
            if(!ban[v] && v!=fa[x])
                que[++t] = v, fa[v] = x;
    }
    for(int i = t; i >= 1; --i) {
        x = que[i], msz[x] = max(msz[x], t-sz[x]);
        if((msz[x]<<1) <= t) return x;
        sz[fa[x]] += sz[x], msz[fa[x]] = max(msz[fa[x]], sz[x]);
    }
    assert(false);
    return 0;
}

int id[maxn+5];
vector<int> cnt[maxn+5], val[maxn+5];
void Mul(int a, int b) {
    int l1 = cnt[a].size();
    int l2 = val[b].size();
    int len = 1;
    while(len < l1+l2-1) len <<= 1;
    assert(len<=maxL);
    REP(i,0,l1-1) A[i] = cnt[a][i];
    REP(i,l1,len-1) A[i] = 0;
    REP(i,0,l2-1) B[i] = val[b][i];
    REP(i,l2,len-1) B[i] = 0;
    NTT(A,len,1), NTT(B,len,1);
    REP(i,0,len-1) A[i] = A[i]*B[i] % P;
    NTT(A,len,-1);
    REP(i,1,len-1) ans = (ans + A[i] * inv[i]) % P;
}

void dfs(int x, int p, int d, int sum, vector<int> &c, vector<int> &v) {
    sum = (sum + ai[x]) % P;
    for(auto y : G[x]) if(y!=p && !ban[y]) {
        dfs(y, x, d+1, sum, c, v);
    }
    if(d+1 > c.size()) {
        c.resize(d+1);
        v.resize(d+1);
    }
    ++c[d], v[d] = (v[d]+sum) % P;
}

void Solve(int x) {
    x = FindRoot(x);
    int tot = 1;
    cnt[0].resize(1), val[0].resize(1);
    cnt[0][0] = 1, val[0][0] = ai[x];
    id[0] = 0;
    for(auto y : G[x]) if(!ban[y]) {
        cnt[tot].clear(), val[tot].clear();
        dfs(y, x, 1, 0, cnt[tot], val[tot]);
        id[tot] = tot;
        ++tot;
    }
    sort(id, id+tot, [&](const int &a, const int &b){ return cnt[a].size() < cnt[b].size(); });
    for(int i = 0; i+1 < tot; ++i) {
        Mul(id[i], id[i+1]);
        Mul(id[i+1], id[i]);
        for(int k = 0; k < cnt[id[i+1]].size(); ++k)
            val[id[i+1]][k] = (val[id[i+1]][k] + LL(ai[x])*cnt[id[i+1]][k]) % P;
        for(int k = 0; k < cnt[id[i]].size(); ++k) {
            cnt[id[i+1]][k] = (cnt[id[i+1]][k] + cnt[id[i]][k]) % P;
            val[id[i+1]][k] = (val[id[i+1]][k] + val[id[i]][k]) % P;
        }
        cnt[id[i]].clear(), val[id[i]].clear();
    }
    cnt[id[tot-1]].clear(), val[id[tot-1]].clear();
    ban[x] = 1;
    for(auto y : G[x]) if(!ban[y]) Solve(y);
}

int main() {
    inv[1] = 1;
    REP(i,2,maxn) inv[i] = (P - P/i) * inv[P%i] % P;
    scanf("%d", &n);
    REP(i,1,n) scanf("%d", ai+i);
    for(int i = 1, u,v; i < n; ++i) {
        scanf("%d%d", &u, &v);
        G[u].PB(v);
        G[v].PB(u);
    }
    Solve(1);
    ans = ans*2 % P;
    printf("%lld\n", ans);
    return 0;
}

回到题目列表

Problem E. 数独挑战

Input file: standard input
Output file: standard output
数独是⼀种填数字游戏,英⽂名叫 Sudoku,起源于瑞⼠,上世纪 70 年代由美国⼀家数学逻辑游戏杂志⾸先发表,名为 Number Place,后在⽇本流⾏,1984 年将 Sudoku 命名为数独,即“独⽴的数字”的缩写,意思是“在每⼀格只有⼀个数字”。2004 年,曾任中国⾹港⾼等法院法官的⾼乐德 (Wayne Gould)把这款游戏带到英国,成为英国流⾏的数学智⼒拼图游戏。⼀组数独的解玩家需要根据 9 × 9 盘⾯上的已知数字,推理出所有剩余位置的数字,并满⾜每⼀⾏、每⼀列、每⼀个粗线九宫格内的数字包含有 1-9 的数字,且不重复。现在给你⼀个数独,请你解答出来。每个数独保证有且只有⼀个解。
在这里插入图片描述
Input
输⼊仅⼀组数据,共 9 ⾏ 9 列,表⽰初始数独(其中 0 表⽰数独中的空位)。
Output
输出共 9 ⾏ 9 列,表⽰数独的解。注意⾏末没有空格。
Example
standard input standard output
5 3 0 0 7 0 0 0 0
6 0 0 1 9 5 0 0 0
0 9 8 0 0 0 0 6 0
8 0 0 0 6 0 0 0 3
4 0 0 8 0 3 0 0 1
7 0 0 0 2 0 0 0 6
0 6 0 0 0 0 2 8 0
0 0 0 4 1 9 0 0 5
0 0 0 0 8 0 0 7 9
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9

扫描二维码关注公众号,回复: 5968511 查看本文章
#include<cstdio>
using namespace std;

int v[100][2],map[10][10];

bool judge(int x,int y,int k)
{
	for(int i=0;i<9;i++)
		if(map[i][y]==k||map[x][i]==k)
			return false;
	int dx=x-x%3,dy=y-y%3;
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
			if(map[dx+i][dy+j]==k)
				return false;
	return true;
}

int dfs(int n)
{
	if(n<0) return 1;
	for(int i=1;i<=9;i++)
	{
		int x=v[n][0],y=v[n][1];
		if(judge(x,y,i))
		{
			map[x][y]=i;
			if(dfs(n-1)) return 1;
			map[x][y]=0;
		}
	}
	return 0;
}

int main()
{
	int num=0;
	for(int i=0;i<9;i++)
		for(int j=0;j<9;j++)
		{
			scanf("%d",&map[i][j]);
			if(map[i][j]==0)
			{
				v[num][0]=i;
				v[num++][1]=j;
			}
		}
	dfs(num-1);
	for(int i=0;i<8;i++)
	{
		for(int j=0;j<8;j++)
			printf("%d ",map[i][j]);
		printf("%d\n",map[i][8]);
	}
	for(int j=0;j<8;j++)
		printf("%d ",map[8][j]);
	printf("%d",map[8][8]);
	return 0;
}
#include <bits/stdc++.h>
using namespace std;

#define MEM(x,y) memset(x,y,sizeof(x))

const int MM = 730 * 330;
int N, M, cnt;
int L[MM], R[MM], D[MM], U[MM], S[MM], C[MM], Row[MM], H[730], ans[90];

void link ( int r, int c ) {
	D[++cnt] = c, U[cnt] = U[c];
	D[U[c]] = cnt, U[c] = cnt;
	S[c] ++, C[cnt] = c, Row[cnt] = r;
	if ( H[r] == -1 ) H[r] = L[cnt] = R[cnt] = cnt;
	else {
		R[cnt] = H[r], L[cnt] = L[H[r]];
		R[L[H[r]]] = cnt, L[H[r]] = cnt;
	}
}

void get ( int i, int j, int k, int &r, int &c1, int &c2, int &c3, int &c4 ) {
	r = ( i*9 + j ) * 9 + k;
	c1 = i*9 + j + 1;
	c2 = 9*9 + i*9 + k;
	c3 = 9*9*2 + j*9 + k;
	c4 = 9*9*3 + ( (i/3) * 3 + (j/3) ) * 9 + k;
}

inline void LINK ( int i, int j, int k ) {
	int r, c1, c2, c3, c4;
	get ( i, j, k, r, c1, c2, c3, c4 );
	link(r,c1),link(r,c2),link(r,c3),link(r,c4);
}

bool init () {
	char ch;
	N = 728, M = 324;
	for ( int i = 0; i <= M; ++ i ) {
		L[i] = i-1, R[i] = i+1;
		U[i] = D[i] = i;
		S[i] = 0;
	}
	L[0] = M, R[M] = 0, cnt = M;
	MEM(H,-1);
	for ( int i = 0; i < 9; ++ i ) {
		for ( int j = 0; j < 9; ++ j ) {
			if ( ! ( cin >> ch ) ) return false;
			if ( ch == '0' ) {
				for ( int k = 1; k <= 9; k ++ ) {
					LINK ( i, j, k );
				}
			} else {
				LINK ( i, j, ch-'0' );
			}
		}
	}
	return true;
}

void DEL( int c ) {
	R[L[c]] = R[c], L[R[c]] = L[c];
	for ( int i = D[c]; i != c; i = D[i] ) {
		for ( int j = R[i]; j != i; j = R[j] ) {
			D[U[j]] = D[j], U[D[j]] = U[j];
			S[C[j]] --;
		}
	}
}

void INS ( int c ) {
	for ( int i = U[c]; i != c; i = U[i] ) {
		for ( int j = L[i]; j != i; j = L[j] ) {
			D[U[j]] = j, U[D[j]] = j;
			S[C[j]] ++;
		}
	}
	R[L[c]] = c, L[R[c]] = c;
}

inline int calR(int r, int n) {
	return (r-1) % n + 1;
}

int DLX( int d ) {
	if ( R[0] == 0 ) {
		int res[10][10];
		for ( int i = 0; i < d; ++ i ) {
			res[ (ans[i]-1) / 81 + 1 ][ (calR(ans[i],81)-1)/9+1 ] = calR(ans[i], 9);
		}
		for ( int i = 1; i <= 9; ++ i ) {
			for ( int j = 1; j <= 9; ++ j ) printf ( j == 1 ? "%d" : " %d", res[i][j]  );
			puts("");
		}
		return 1;
	}
	int mi = 0x7ffffff, c = 0;
	for ( int i = R[0]; i != 0; i = R[i] ) {
		if ( S[i] < mi ) mi = S[i], c = i;
	}
	DEL(c);
	for ( int i = D[c]; i != c; i = D[i] ) {
		ans[d] = Row[i];
		for ( int j = R[i]; j != i; j = R[j] ) DEL(C[j]);
		if ( DLX( d + 1 ) ) return 1;
		for ( int j = L[i]; j != i; j = L[j] ) INS(C[j]);
	}
	INS(c);
	return 0;
}

int read () {
	if ( scanf ( "%d%d", &N, &M ) == EOF ) return 0;
	return 1;
}

void prs () {
	static int f = 0;
	if ( f ++ ) puts("");
	DLX(0);
}

int main () {
	char ch;
	while ( init() ) {
		prs();
	}
	return 0;
}

回到题目列表

Problem F. 翻牌游戏

Input file: standard input
Output file: standard output
胡⽼师最近迷上了⼀款经典⼩游戏——翻牌。桌⾯上有 n 个互不相同的对⼦组成的 2n 张扑克牌,背⾯朝上放在桌⾯上。每⼀轮,胡⽼师会选择两张不同的牌翻开,如果这两张牌相同,则将它们消除;如果不同,则将它们盖回去。需要注意的是:胡⽼师总是先选择两张牌,再同时将它们翻开。胡⽼师有个外号叫⼈形 AI,他拥有超强的记忆⼒,所以他可以记住任何他曾经翻开过的牌的位置。他现在想知道,在这种情况下,消除所有牌需要的最少期望轮数是多少。
Input
第⼀⾏是⼀个整数 T (1 ≤ T ≤ 20000),表⽰数据组数。接下来 T ⾏,每⾏仅包含⼀个正整数 n (1 ≤ n ≤ 5000),表⽰⼀共有 n 个对⼦(即 2n 张扑克牌)。
Output
输出共 T ⾏。对每组数据,输出⼀⾏⼀个恰有两位⼩数的浮点数,表⽰消除所有牌所需最少期望轮数精确到⼩数点后
两位的结果。
Example
standard input standard output
1
2
3.00
Notes
以下叙述 n = 2 时的最优策略。
(1) 随机选择两张扑克牌。有 1/3 概率两张牌相同,直接消除,跳⾄ (4);有 2/3 概率不同,跳⾄ (2)。
(2) 选择⼀张已经选过的扑克牌,和⼀张没有选过的扑克牌。有 1/2 的概率相同,直接消除,跳⾄ (4);有1/2 的概率不同,跳⾄ (3)。
(3) 这时已经知道所有扑克牌的图案,选择⼀个对⼦消除,跳⾄ (4)。
(4) 消除最后两张扑克牌。
数学期望为:E =1/3 × 2 + 1/3 × 3 + 1/3 × 4 = 3.00。

#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef double DB;

int main() {
    int _; scanf("%d", &_);
    while(_--) {
        int n; scanf("%d", &n);
        printf("%.2f\n", DB(n*2-1));
    }
    return 0;
}
// CC!
#include<bits/stdc++.h>

using namespace std;

typedef double DB;

const int maxn = 5000;

DB f[maxn+5][maxn+5];
bool vi[maxn+5][maxn+5];

DB dfs(int n, int m) {
    if(vi[n][m]) return f[n][m];
    vi[n][m] = 1;
    if(n==0 && m==0) return f[n][m] = 0;

    f[n][m] = 1e9;
    int hd = n*2+m;

    // nn策略
    if(hd>=2) {
        DB tmp = 0;
        if(n>=1) tmp += DB(n*2.0) / (hd*(hd-1.0)) * (dfs(n-1,m)+1); // 2张未知且相同
        if(n>=2) tmp += (2.0*n*(2.0*n-2.0)) / DB(hd*(hd-1.0)) * (dfs(n-2,m+2)+1); // 2张未知但不同
        if(m>=2) tmp += DB(m*(m-1.0)) / DB(hd*(hd-1.0)) * (dfs(n,m-2)+3); // 2张已知
        if(n>=1 && m>=1) tmp += DB(4.0*n*m) / DB(hd*(hd-1.0)) * (dfs(n-1,m)+2); // 1张已知1张未知
        f[n][m] = min(f[n][m], tmp);
    }

    // nm策略
    if(m>0) {
        DB tmp = 0;
        tmp += 1.0 / DB(hd) * (dfs(n,m-1)+1); // 2张已知且相同
        if(m>=2) tmp += (m-1.0) / DB(hd) * (dfs(n,m-1)+2); // 2张已知但不同
        if(n>=1) tmp += (2.0*n) / DB(hd) * (dfs(n-1,m+1)+1); // 1张未知1张已知
        f[n][m] = min(f[n][m], tmp);
    }

    return f[n][m];
}

int main() {
    int _; scanf("%d", &_);
    while(_--) {
        int n; scanf("%d", &n);
        printf("%.2f\n", dfs(n,0) );
    }
    return 0;
}

回到题目列表

Problem G. 铁树开花

Input file: standard input
Output file: standard output
东东是⼀名替⾝使者,但他那个名叫“铁树开花”的替⾝能⼒有点⽆聊——简单来说,“铁树开花”可以使⼀棵花的种⼦⽴刻发芽⻓⼤并开花。现在有⼀个 n 个点 C 条边的⽆向图,图中结点编号为 1 ∼ n,且每条边边权均为 1。在这个图上分布有 m 颗花的种⼦,编号为 1 ∼ m,每颗种⼦可以被两个参数 xi, di(其中 di ∈ {0, 1, 2})描述,表⽰编
号为 i 的种⼦埋在了编号为 xi 的结点内,且当其开花时,所有到结点 xi 的最短路⻓度不超过 di 的结点都会被这朵花覆盖。东东可以任意指定⼀个 m 的排列 p1, p2, · · · , pm,然后依次对编号为 p1, p2, · · · , pm 的种⼦使⽤“铁树开花”能⼒,于是⽆向图中的每⼀个结点最终都可能被第 1 ∼ m 中的某⼀朵花覆盖。需要注意的是,某个结点最终被哪朵花覆盖,取决于最后覆盖它的是哪朵花。 ⽐如在执⾏整⼀个开花顺序的过程中,结点 u 先后被编号为 4, 8, 2 的花覆盖,那么我们称结点 u 最终是被编号为 2 的花覆盖的。我们记最终被编号为 i 的花覆盖的结点的个数为 cnti。东东很⽆聊,他想找到这样的⼀个排列,使得按照这个排列使⽤“铁树开花”能⼒后,能达成下⾯这个⽬标:max{ min1≤i≤m{cnti}}也就是说,要找到⼀个排列 P,使得在考虑每⼀个可能的排列下最⼩的那个 cnti 时,排列 P 对应的那个min1≤i≤m(cnti) 是最⼤的。显然这样的排列 P 可能有很多个,所以东东想要找出在所有满⾜上述条件的排列 P 中,字典序最⼩的那个。东东⾃然是⼀眼看穿了答案,于是把这个问题扔给你来做。他为了不让你难过,保证了给出的⽆向图⼀定是⼀棵树。他希望你求出 max{min1≤i≤m{cnti}},以及在能达成这⼀⽬标且字典序最⼩的那个排列下,每朵花的 cnti 值。
Input
第⼀⾏两个整数 n, m(1 ≤ n ≤ 105, m ≤ 2 × 104),分别表⽰树的结点个数和种⼦个数。
接下来 n n 1 ⾏给出了树的结构,每⾏两个整数 u, v (1 ≤ u, v ≤ n, u ̸= v),表⽰树中有⼀条连接结点u, v 的边。最后是 m ⾏,其中第 i ⾏是两个整数 xi, di (1 ≤ xi ≤ n, di ∈ {0, 1, 2}),表⽰第 i 颗种⼦(第 i 朵花)的参数,意义如题⽬描述中所述。
Output
输出共 m + 1 ⾏。第⼀⾏输出 max{min1≤i≤m{cnti}} 的值。接下来 m ⾏,每⾏输出⼀个整数,其中第 i ⾏为在满⾜条件且字典序最⼩的开花顺序下的 cnti 值。
Example
standard input standard output
10 3
3 1
5 2
2 4
1 2
6 3
3 7
4 8
10 7
9 5
5 2
2 0
3 1
1
3
1
4

#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define lson (x<<1)
#define rson (x<<1|1)
#define mid ((l+r)>>1)
typedef long long LL;
typedef double DB;
typedef pair<int,int> pii;

const int maxn = 1e5;
const int maxM = 2e4;
const int maxnode = maxn<<2;

int n, m;
int DFN = 0, dfn[maxn+5], fa[maxn+5];
vector<int> vec[maxn+5];
int L1[maxn+5], R1[maxn+5], L2[maxn+5], R2[maxn+5];
int pt[maxM+5], dd[maxM+5];
int cnt[maxM+5];
bool dead[maxM+5];
vector<int> G[maxn+5];
priority_queue<pair<int,int>> q;

int tr[maxnode+5], lz[maxnode+5];
set<int> cov[maxnode+5];

void dfs(int x, int p, int dis) {
    fa[x] = p;
    vec[dis].PB(x);
    for(auto y : G[x]) if(y!=p) {
        dfs(y,x, dis+1);
    }
}

void dfs2(int x, int p) {
    L1[x] = L2[x] = n+1;
    R1[x] = R2[x] = 0;
    for(auto y : G[x]) if(y!=p) {
        dfs2(y,x);
        L1[x] = min(L1[x], dfn[y]);
        R1[x] = max(R1[x], dfn[y]);
        L2[x] = min(L2[x], L1[y]);
        R2[x] = max(R2[x], R1[y]);
    }
}

void reBuild(int x, int l, int r) {
    tr[x] = lz[x] = 0;
    assert(cov[x].size()==0);
    if(l<r) {
        reBuild(lson, l, mid);
        reBuild(rson, mid+1, r);
    }
}

void PushDown(int x) {
    lz[lson]+=lz[x], tr[lson]+=lz[x];
    lz[rson]+=lz[x], tr[rson]+=lz[x];
    lz[x] = 0;
}

void Upd(int x, int l, int r, int v) {
    if(tr[x]>1) return;
    if(cov[x].size()) v = *cov[x].begin();
    if(l==r) {
        if(tr[x]==0) tr[x] = n+10;
        else if(tr[x]==1) {
            assert(1<=v && v<=n);
            ++cnt[v];
            q.push( pair<int,int>(cnt[v],v) );
            tr[x] = n+10;
        }
    }
    else {
        PushDown(x);
        Upd(lson, l, mid, v);
        Upd(rson, mid+1, r, v);
        tr[x] = min(tr[lson], tr[rson]);
    }
}

void Add(int x, int l, int r, int ll, int rr, int v) {
    if(ll<=l && r<=rr) {
        ++tr[x], ++lz[x];
        cov[x].insert(v);
    }
    else {
        PushDown(x);
        if(ll<=mid) Add(lson, l, mid, ll, rr, v);
        if(mid<rr) Add(rson, mid+1, r, ll, rr, v);
        tr[x] = min(tr[lson], tr[rson]);
    }
}

void Sub(int x, int l, int r, int ll, int rr, int v) {
    if(ll<=l && r<=rr) {
        --tr[x], --lz[x];
        auto it = cov[x].find(v);
        assert(it != cov[x].end());
        cov[x].erase(it);
    }
    else {
        PushDown(x);
        if(ll<=mid) Sub(lson,l, mid, ll, rr, v);
        if(mid<rr) Sub(rson, mid+1, r, ll, rr, v);
        tr[x] = min(tr[lson], tr[rson]) + lz[x];
    }
}

void Mod(int l, int r, int v, int o) {
    if(l>r) return;
    if(o==1) Add(1, 1, n, l, r, v);
    else Sub(1, 1, n, l, r, v);
}

void Draw(int id, int o) {
    assert(o==1 || o==-1);
    int x = pt[id], kk = dd[id];
    if(kk==0) Mod(dfn[x], dfn[x], id, o);
    else if(kk==1) {
        Mod(L1[x], R1[x], id, o);
        Mod(dfn[x], dfn[x], id, o);
        if(fa[x]!=0) Mod(dfn[fa[x]], dfn[fa[x]], id, o);
    }
    else {
        Mod(L1[x], R1[x], id, o);
        Mod(L2[x], R2[x], id, o);
        if(fa[x]!=0) {
            int y = fa[x];
            Mod(L1[y], R1[y], id, o);
            Mod(dfn[y], dfn[y], id, o);
            if(fa[y]!=0) Mod(dfn[fa[y]], dfn[fa[y]], id, o);
        }
        else Mod(dfn[x], dfn[x], id, o);
    }
}

priority_queue<int> q2;

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1, u,v; i < n; ++i) {
        scanf("%d%d", &u, &v);
        G[u].PB(v); G[v].PB(u);
    }
    dfs(1,0,1);
    REP(i,1,n) {
        for(auto x : vec[i]) dfn[x] = ++DFN;
    }
    dfs2(1,0);
    REP(i,1,m) {
        scanf("%d%d", pt+i, dd+i);
        Draw(i,1);
    }
    REP(i,1,m) q.push( pair<int,int>(0,i) );
    Upd(1, 1, n, -1);
    int ans = n+10;
    REP(i,1,m) {
        while(dead[q.top().se] || cnt[q.top().se]!=q.top().fi) q.pop();
        ans = min(ans, q.top().fi);
        int x = q.top().se; q.pop();
        assert(1<=x && x<=m);
        dead[x] = 1;
        Draw(x,-1);
        Upd(1, 1, n, -1);
    }

    reBuild(1,1,n);
    while(!q.empty()) q.pop();
    REP(i,1,m) cnt[i] = 0, dead[i] = 0, Draw(i,1), q.push( pair<int,int>(0,i) );
    Upd(1, 1, n, -1);
    int gao = 0;
    REP(i,1,m) {
        while(gao < m) {
            while(dead[q.top().se] || cnt[q.top().se]!=q.top().fi) q.pop();
            if(q.top().fi < ans) break;
            q2.push( -q.top().se );
            dead[q.top().se] = 1;
            q.pop(); ++gao;
        }
        int x = -q2.top();
        q2.pop();
        Draw(x,-1);
        Upd(1, 1, n, -1);
    }

    printf("%d\n", ans);
    int tmp_sum = 0;
    REP(i,1,m) printf("%d\n", cnt[i]), tmp_sum += cnt[i];

    return 0;
}

回到题目列表

Problem H. Parco_Love_GCD

Input file: standard input
Output file: standard output
众所周知,在算法竞赛中,出题⼈对他出的题的难度往往存在错误的估计。⽐如出题⼈本想出个中等题,没想到却出成了简单题;本想出个⾃闭题,结果数据太⽔变成了签到题。因此,为了让⼀场⽐赛能有良好的体验,有⼀个靠谱的验题⼈是⾮常重要的。CC 出好题⽬后,便拿给⼩⻢哥看。不出所料,这些题⽬⼩⻢哥全都是看⼀眼就会做了。⽽且,⼩⻢哥觉得这些题对于参赛选⼿来说也太⽔了(5 个签到题哦〜)。为了避免发⽣全场⼗⼏个队 AK 这种紧急事态,⼩⻢哥决定把⼀道签到题改成简单题,于是便有了现在这个题⽬。⼩⻢哥⾮常喜欢数论,尤其钟爱“最⼩公倍数”(Greatest Common Divisor,简称 GCD)这⼀概念,因此他打算出⼀道和 GCD 有关的题⽬。⼩⻢哥⾸先给出了含 n 个正整数的序列 a1, a2, · · · , an,然后让你考虑该序列的所有⼦区间的数对应的 GCD 值,也就是说考虑所有 gcd(al, · · · , ar) 的值。显然,这样
的值⼀共有 n(n+1)2 个。⼀个中⼆出题⼈可能会让你求这 n(n+1)2 数中第 k ⼤的数,但幸运的是,⼩⻢哥是个正经出题⼈,因此它只让你求这 n(n+1)2 个数之和模 109 + 7 的结果。也就是要求下⾯这个式⼦:
ans = (∑nl=1∑nr=lgcd(al, · · · , ar)) mod (109 + 7)
⼩⻢哥在此预祝⼤家 AK 〜
Input
第⼀⾏是⼀个整数 n (1 ≤ n ≤ 5 × 105),表⽰数的个数。接下来⼀⾏给出 n 个整数,其中第 i 个整数 ai满⾜ 1 ≤ ai ≤ 109。
Output
输出⼀⾏⼀个整数,表⽰ ans 的值。
Example
standard input standard output
5
16 4 7 21 3
72

#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef double DB;

const int maxn = 5e5;
const int maxV = 1e9;
const int P = 1e9+7;

LL Gcd(LL a, LL b) { return b ? Gcd(b,a%b) : a; }

map<int,int> s1, s2;
int n, ai[maxn+5];
LL ans = 0;

inline void Add(LL &a, LL b) { a+=b; if(a>=P) a-=P; }

void Solve(int l, int r) {
    if(l==r) { Add(ans, ai[l]); return; }
    int mid = (l+r)>>1;
    s1.clear();
    s2.clear();

    int now = 0;
    for(int i = mid; i >= l; --i) {
        now = Gcd(now, ai[i]);
        ++s1[now];
    }

    now = 0;
    for(int i = mid+1; i <= r; ++i) {
        now = Gcd(now, ai[i]);
        ++s2[now];
    }

    for(auto pp : s1) {
        for(auto qq : s2) {
            Add(ans, Gcd(pp.fi, qq.fi) * pp.se % P * qq.se % P);
        }
    }

    Solve(l, mid);
    Solve(mid+1, r);
}

int main() {
    scanf("%d", &n);
    assert(1<=n && n<=maxn);
    REP(i,1,n) scanf("%d", ai+i);
    Solve(1,n);
    printf("%lld\n", ans);
    return 0;
}
#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef double DB;

const int maxn = 5e5;
const int Step = 17;
const int P = 1e9+7;

LL Gcd(LL a, LL b) { return b ? Gcd(b,a%b) : a; }

int n, ai[maxn+5];
int st[maxn+5][Step+2];
int len[maxn+5];

int Que(int l, int r) {
    int k = len[r-l+1];
    return Gcd( st[l][k], st[r-(1<<k)+1][k] );
}

int main() {
    scanf("%d", &n);
    REP(i,1,n) scanf("%d", ai+i), st[i][0] = ai[i];
    for(int k = 1; (1<<k) <= n; ++k) {
        for(int i = 1; i+(1<<k)-1 <= n; ++i) {
            st[i][k] = Gcd(st[i][k-1], st[i+(1<<(k-1))][k-1]);
        }
    }
    for(int i = 1; i <= n; ++i) {
        len[i] = len[i-1];
        if((1<<(len[i]+1)) < i) ++len[i];
    }
    LL ans = 0;
    REP(i,1,n) {
        int now = 0;
        for(int k = i, nx; k <= n; k = nx+1) {
            now = Gcd(now, ai[k]);
            int l = k+1, r = n;
            nx = k;
            while(l<=r) {
                int mid = (l+r)>>1;
                int tmp = Que(k,mid);
                if(tmp%now==0) nx = mid, l = mid+1;
                else r = mid-1;
            }
            ans = (ans + LL(nx-k+1) * now) % P;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

回到题目列表

Problem I. 炒股

Input file: standard input
Output file: standard output
攒机⼀时爽,⼀直攒机⼀直爽。沉迷攒机的胡⽼师很快就发现,他每天只能靠吃泡⾯过活了。为了改善伙⻝,同时继续攒机⼤业,胡⽼师决定下海炒股。胡⽼师有特别的炒股技巧。⾸先他会选定⼀⽀他看好的股票,然后永远只买这⼀⽀股票。此外,胡⽼
师每天要么只买⼊股票要么只卖出股票,且出于某种不为⼈知的原因,胡⽼师⼿上最多只能持有 1 股的股票。胡⽼师每天会根据当天的股价及⼿上的持股数决定是买⼊还是卖出股票,需要注意的是,只要胡⽼师选择了买⼊或卖出,那么⼀定可以按当天的价格买⼊或卖出股票。炒股需要本⾦,但胡⽼师的钱都拿去攒机了,于是他去找 CC 借了⼀⼤笔钱(这笔钱可以视为⽆穷多),并约定 n 天之后归还。另⼀⽅⾯,为最⼤化 n 天内的收益,胡⽼师通过不为⼈知的 py 交易获取了接下来 n 天每天的股票价格。在⼀开始,胡⽼师⼿上没有持有股票。胡⽼师想知道,在 n 天之后他最多能靠炒股赚多少钱。
Input
第⼀⾏是⼀个整数 n (1 ≤ n ≤ 5 × 105),表⽰天数。接下来 n ⾏给出了接下来 n 天内胡⽼师看好的股票每天的价格,其中第 i ⾏是⼀个整数 ai (1 ≤ ai ≤ 106),表⽰该股票在第 i 天的价格。
Output
输出⼀⾏⼀个整数 ans,表⽰ n 天之后胡⽼师最多能赚的钱数。
Example
standard input standard output
5
1
3
2
6
4
6

#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef double DB;

const int maxn = 5e5;
const int maxV = 1e6;

int n, ai[maxn+5];

int main() {
    scanf("%d", &n);
    LL ans = 0;
    REP(i,1,n) scanf("%d", ai+i);
    REP(i,2,n) if(ai[i]>ai[i-1]) ans += ai[i]-ai[i-1];
    printf("%lld\n", ans);
    return 0;
}

回到题目列表

Problem J. 单⾝狗救星

Input file: standard input
Output file: standard output
为了提⾼华南理⼯⼤学的脱单率,沈⽼师办了个相亲交友活动(具体形式可以参考⾮诚勿扰),⾯向⼴⼤华⼯学⼦。参加活动的每⼀位同学都有⼀个帅⽓/美丽程度 B 以及⼀个聪明程度 I。或许你会觉得,找对象⾃然是应该找越好看且越聪明的,但对万年单⾝的华⼯学⼦来说,他们只想找最适合⾃⼰的。考虑两位同学Alice 和 Bob。Alice 的帅⽓/美丽程度是 B1,聪明程度是 I1;Bob 的帅⽓/美丽程度是 B2,聪明程度是I2。则 Alice 和 Bob 作为伴侣来说的适合度按如下公式计算:
B1 I2
B2 I1
+
B2 I2
B1 I1

B1 B2
B2 B1

现在,台上有 n 位男同学(类似⾮诚勿扰的规则),编号为 1 ∼ n,他们的帅⽓程度与聪明程度都是已知的。台下有 m 位⼥同学,每位⼥同学也有⾃⼰的美丽度与聪明度,⽽她们都想从台上的 n 位男同学中选择⼀个最适合⾃⼰的⼈做对象,也就是说想找⼀个对⾃⾝来说适合度最⼤的对象。如果有多位男同学的适合度都是最⼤的,则选择编号较⼩的做对象。沈⽼师想知道,台下的每位⼥同学,究竟打算选谁做为⾃⼰的对象。需要注意的是,由于现在还只是思考阶段,没有正式开始选对象过程,所以有多位⼥同学选择同⼀位男同学是可能的。
Input
第⼀⾏是两个整数 n, m (1 ≤ n, m ≤ 105),分别表⽰男同学⼈数及⼥同学⼈数。
接下来 n ⾏每⾏两个整数,其中第 i ⾏是 Bi, Ii ((109 ≤ Bi, Ii ≤ 109),表⽰表⽰第 i 位男同学的帅⽓程度及聪明程度。数据保证不存在两位男同学,他们的帅⽓及美丽程度完全相同。此外,也不存在这样的三位男同学 a,b,c,使得 a 和 b 的适合度与 b 和 c 的适合度相同。接下来 m ⾏每⾏两个整数,其中第 i ⾏是 B′i, I′i((109 ≤ B′i, I′i ≤ 109),表⽰表⽰第 i 位⼥同学的美丽程度及聪明程度。数据保证不存在⼀名⼥同学与⼀名男同学,他们的聪明程度的绝对值相同。
Output
输出共 m ⾏,每⾏⼀个整数,其中第 i ⾏表⽰第 i 位⼥同学打算选择的男同学的编号。
Example
standard input standard output
4 2
1 1
2 4
5 4
3 2
4 1
0 2
3
2

#include <bits/stdc++.h>
#define ll long long

using namespace std;

struct node {
    ll x, y;
    int ty, id;
    node() {}
    node(ll _x, ll _y, int _ty, int _id)
        : x(_x), y(_y), ty(_ty), id(_id) {}
    void init(int _ty, int _id) {
        scanf("%lld%lld", &x, &y);
        ty = _ty;
        id = _id;
    }
    node operator-(const node &b) const {
        return node(x - b.x, y - b.y, 0, 0);
    }
    ll operator^(const node &b) const {
        return x * b.y - y * b.x;
    }
    bool operator<(const node &b) const {
        return x == b.x ? y < b.y : x < b.x;
    }
};

const int MAXN = 1e5 + 10;
node a[MAXN], b[MAXN], cc[MAXN << 1];
int ans[MAXN], que[MAXN], lim;

bool judge(node a, node b, node c) {
    ll tmp = (b - a) ^ (a - c);
    return tmp == 0 ? c.id < b.id : tmp > 0;
}

void solve() {
    int las = 0;
    for (int i = 1; i <= lim; i++) {
        if (cc[i].ty == 1) {
            while (las > 1 && ((cc[i] - cc[que[las]]) ^ (cc[que[las]] - cc[que[las - 1]])) > 0)
                las--;
            que[++las] = i;
        } else {
            if (!las)
                continue;
            int l = 1, r = las;
            while (l < r) {
                int mid = (l + r) >> 1;
                if (((cc[i] - cc[que[mid + 1]]) ^ (cc[que[mid + 1]] - cc[que[mid]])) > 0)
                    r = mid;
                else
                    l = mid + 1;
            }
            if (ans[cc[i].id] == -1 || judge(b[cc[i].id], a[ans[cc[i].id]], a[cc[que[l]].id]))
                ans[cc[i].id] = cc[que[l]].id;
        }
    }
}

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    lim = n + q;
    for (int i = 1; i <= lim; i++) {
        if (i <= n) {
            a[i].init(1, i);
            cc[i] = a[i];
        } else {
            b[i - n].init(2, i - n);
            cc[i] = b[i - n];
        }
    }
    memset(ans, -1, sizeof(ans));
    sort(cc + 1, cc + lim + 1);
    solve();
    reverse(cc + 1, cc + lim + 1);
    for (int i = 1; i <= lim; i++) {
        cc[i].x = -cc[i].x;
        cc[i].y = -cc[i].y;
    }
    solve();
    for (int i = 1; i <= q; i++)
        printf("%d\n", ans[i]);
}

回到题目列表

Problem K. Parco_Love_String

Input file: standard input
Output file: standard output
众所周知,在算法竞赛中,出题⼈对他出的题的难度往往存在错误的估计。⽐如出题⼈本想出个简单题,没想到却出成了重坑细节题;本想出个中等难度题,结果却变成了防 AK 题。因此,为了让⼀场⽐赛能有良好的体验,有⼀个靠谱的验题⼈是⾮常重要的。
CC 出好题⽬后,便拿给⼩⻢哥看。不出所料,这些题⽬⼩⻢哥全都是看⼀眼就会做,但他觉得这对于参赛选⼿来说还是有点难。为了避免 CC 被喷成毒瘤出题⼈,⼩⻢哥准备加⼀道签到题。⼩⻢哥有⼀个只由⼩写字⺟组成的字符串 S,他会问你⼀些关于这个字符串的问题。⼩⻢哥每次提问时会给你⼀个整数 i,意思是要把字符串 S 在第 i 和第 i + 1 个字符之间切断,这样便得到两个新串S[1, i] 及 S[i + 1, |S|]。记 A = S[1, i],B = S[i + 1, |S|]。现在,先考虑 A 的所有连续⼦串,再考虑 B 的所有连续⼦串,⼩⻢哥问你 A,B 之间相同⼦串对有多少个。也就是说,⼩⻢哥想让你计算下⾯这个式⼦:
ans =∑1≤l1≤r1≤|A|∑1≤l2≤r2≤|B|[ A[l1, · · · , r1] == B[l2, · · · , r2] ]
其中,求和式右边的式⼦是这样⼀个函数:[x] =1 (x = T rue)0 (x = F alse)
你能解决⼩⻢哥的问题,并签到成功吗?
Input
第⼀⾏是⼀个字符串 S,保证只由⼩写字⺟组成,且 1 ≤ |S| ≤ 1000。第⼆⾏是⼀个正整数 T (1 ≤ T ≤ 105),表⽰询问次数。接下来 T ⾏,每⾏⼀个整数 i (1 ≤ i ≤ n n 1),表⽰⼀个询问。
Output
对于每个询问,输出⼀⾏⼀个整数表⽰对应询问的 ans,意义如题⽬描述中所述。
Example
standard input standard output
ababa
4
1
2
3
4
2
4
4
2
Notes
对于 i = 3 这个询问,原串被拆分成 A = aba,B = ba。此时 A 的所有⼦串为:a,a,b,ab,ba,aba;B 的所有⼦串为:a,b,ba。
因此 A,B 之间相同⼦串对为:(a, a),(a, a),(b, b),(ba, ba),共计 4 个。

#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef unsigned long long ULL;
typedef double DB;

const int maxn = 1000;

typedef unsigned long long Key;
const ULL base = 19260817;

ULL pw[maxn+5], ha[maxn+5];

int n;
char s[maxn+5];
unordered_map<Key,int> MapL[maxn+5], MapR[maxn+5];
LL f[maxn+5];

inline Key GetKey(int l, int r) {
    assert(1<=l && l<=r && r<=n);
    Key tmp1 = ha[r] - ha[l-1]*pw[r-l+1];
    return tmp1;
}

int main() {
    pw[0] = 1;
    REP(i,1,maxn) {
        pw[i] = pw[i-1]*base;
    }
    scanf("%s", s+1);
    n = strlen(s+1);
    REP(i,1,n) {
        ha[i] = ha[i-1] * base + ULL(s[i]-'a');
    }
    for(int i = 1; i <= n; ++i) {
        for(int k = i; k <= n; ++k) {
            ++MapR[k-i+1][ GetKey(i,k) ];
        }
    }
    for(int k = 1; k < n; ++k) {
        f[k] = f[k-1];
        for(int j = k; j <= n; ++j) {
            Key tmp = GetKey(k,j);
            if(MapL[j-k+1].find(tmp) != MapL[j-k+1].end()) {
                f[k] -= MapL[j-k+1][tmp];
            }
            --MapR[j-k+1][tmp];
        }
        for(int i = 1; i <= k; ++i) {
            Key tmp = GetKey(i,k);
            if(MapR[k-i+1].find(tmp) != MapR[k-i+1].end()) {
                f[k] += MapR[k-i+1][tmp];
            }
            ++MapL[k-i+1][tmp];
        }
    }
    int _; scanf("%d", &_);
    while(_--) {
        int kk; scanf("%d", &kk);
        printf("%lld\n", f[kk]);
    }
    return 0;
}
#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef double DB;

const int maxn = 1000, c_size = 26;

struct State {
    int val;
    LL sum, occ;
    int par, go[c_size];
    void init() { val = 0, sum = 0, occ = 0, par = -1, mem(go,-1); }
}que[maxn*2+5];
int root, last;
int tot;

void Extend(int w) {
    int p = last, np = tot++;
    que[np].init();
    que[np].val = que[p].val+1;
    que[np].occ = 1;
    while(p!=-1 && que[p].go[w]==-1)
        que[p].go[w] = np, p = que[p].par;
    if(p == -1) que[np].par = root;
    else {
        int q = que[p].go[w];
        if(que[p].val+1 == que[q].val) que[np].par = q;
        else {
            int nq = tot++;
            que[nq] = que[q];
            que[nq].val = que[p].val+1;
            que[nq].occ = 0;
            que[np].par = que[q].par = nq;
            while(p!=-1 && que[p].go[w]==q)
                que[p].go[w] = nq, p = que[p].par;
        }
    }
    last = np;
}


char str[maxn+5];
LL f[maxn+5];
int cont[maxn+5], S[maxn*2+5];

int main() {
    scanf("%s", str+1);
    int n = strlen(str+1);
    for(int kk = 1; kk < n; ++kk) {
        root = last = 0;
        tot = 1, que[0].init();
        for(int i = 1; i <= kk; ++i) Extend(str[i]-'a');
        for(int i=0; i<=kk; ++i) cont[i] = 0;
        for(int i=0; i<tot; ++i) ++cont[que[i].val];
        for(int i=1; i<=kk; ++i) cont[i] += cont[i-1];
        for(int i=tot-1; i>=1; --i) S[--cont[que[i].val]] = i;
        for(int i = tot-1; i >= 1; --i) {
            int x = S[i];
            que[que[x].par].occ += que[x].occ;
        }
        for(int i = 1; i < tot; ++i) {
            int x = S[i];
            que[x].sum = que[que[x].par].sum;
            que[x].sum += LL(que[x].val-que[que[x].par].val) * que[x].occ;
        }
        int p = root;
        int now = 0;
        f[kk] = 0;
        for(int i = kk+1; i <= n; ++i) {
            int w = str[i]-'a';
            if(que[p].go[w] != -1) {
                p = que[p].go[w];
                ++now;
            }
            else {
                while(p!=-1 && que[p].go[w]==-1) p = que[p].par;
                if(p == -1) p = root, now = 0;
                else {
                    now = que[p].val+1;
                    p = que[p].go[w];
                }
            }
            if(now>0) {
                f[kk] += que[que[p].par].sum;
                f[kk] += LL(now - que[que[p].par].val) * que[p].occ;
            }
        }
    }
    int _; scanf("%d", &_);
    while(_--) {
        int kk; scanf("%d", &kk);
        printf("%lld\n", f[kk]);
    }
    return 0;
}

回到题目列表

Problem L. ⽯头剪⼑布

Input file: standard input
Output file: standard output
⽯头剪⼑布是⼀个⼈⼈都喜欢玩的休闲⼩游戏,其制胜规则如下图。有研究表明,在玩⽯头剪⼑布时,⼈们⼤多倾向于“赢家保持现状输家做出改变的策略”。这意味着,如果你是输家,下⼀轮换应该⽤能打败对⼿的出⼿;如果你是赢家,下⼀轮就不要再使⽤原来的出⼿。对于 CC 来说,这些⼼⾥分析都是⽆所谓的,因为他是灵能⼒者,可以预知对⼿想要出⽯头(Rock),剪⼑(Scissors),还是布(Paper)。CC 想要每轮⽐赛都赢,请你告诉 CC 他每轮应该出什么才能赢。
Input
第⼀⾏是⼀个整数 n (1 ≤ n ≤ 100),表⽰⽐赛轮数。接下来 n ⾏,每⾏⼀个字符串。这个字符串只能是 Rock ,Scissors ,Paper 三者之⼀,表⽰本轮对⼿的出招是⽯头还是剪⼑还是布。
Output
输出共 n ⾏。对对⼿的每个出招,输出⼀⾏⼀个字符串。这个字符串只能是 Rock ,Scissors ,Paper 三者之⼀,表⽰本轮 CC 需要出⽯头还是剪⼑还是布才能赢对⼿。
Example
standard input standard output
3
Scissors
Rock
Paper
Rock
Paper
Scissors

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    string str;
    cin >> n;
    while (n-- > 0) {
        cin >> str;
        if (str == "Scissors") cout << "Rock\n";
        if (str == "Rock") cout << "Paper\n";
        if (str == "Paper") cout << "Scissors\n";
    }
    return 0;
}

回到题目列表

猜你喜欢

转载自blog.csdn.net/qq_43333395/article/details/89321355
今日推荐