Educational Codeforces Round 33 (Rated for Div. 2) 题解

题目链接:http://codeforces.com/contest/893

A. Chess For Three

题意:判断给定的每局的胜者是否符合题意,第一局1和2参赛,3旁观,第二局上一局的胜者和3参赛,上一局的败者旁观,以此类推。

解法:直接模拟,看是否出现矛盾即可。

#include <bits/stdc++.h>
using namespace std;

int main(){
    int n, a=1, b=2, c=3, d;
    scanf("%d", &n);
    while(n--){
        scanf("%d", &d);
        if(d==c){
            return 0*puts("NO");
        }
        if(d==a) swap(b, c);
        else swap(a, c);
    }
    puts("YES");
}

B. Beautiful Divisors

题意:题目给出了一个数n,让你从它的因子里面找出一个最大的美丽的数

解法:枚举美丽的数,然后与判断是否可以整除

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
    LL n;
    scanf("%lld", &n);
    for(int i=18; i>=1; i--){
        if(n%(1LL*((1LL<<i)-1)*(1LL<<(i-1)))==0){
            return 0*printf("%lld\n",((1LL<<i)-1)*(1LL<<(i-1)));
        }
    }
}

C. Rumor

题意:每个字母传递一个人有一个代价,朋友之间传递不需要代价,问所有人都被传到需要多少代价。

解法:dfs维护每个联通快的最小值,然后求和。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
typedef long long LL;
vector <int> G[maxn];
int a[maxn];
bool vis[maxn];
LL ans = 0;
int dfs(int x){
    vis[x] = 1;
    int now = a[x];
    for(int v:G[x]){
        if(vis[v]) continue;
        now = min(now, dfs(v));
    }
    return now;
}
int main(){
    int n, m;
    scanf("%d%d", &n,&m);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
    }
    for(int i=1; i<=m; i++){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1; i<=n; i++){
        if(!vis[i]){
            ans += dfs(i);
        }
    }
    return 0*printf("%lld\n", ans);
}

D. Credit Card

题意:信用卡n天交易记录,第i天可能赚钱,可能赔钱,可以查账,赚钱后,帐户总余额不能超过上限,超过就不符合要求输出-1,查账的时候,不希望余额是负值,如果哪天赔钱后,可以去银行存任意数量的钱,把余额变成大于等于0.希望查账的日子都不会查到负数。

解法:考虑贪心,在每次存钱的时候可以存多点,这样赔钱数比较少的情况下,下次查账也是正值,不用去银行。维护后缀最大值可以办到,后缀最大值写挂了,WA了2发。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 1e5+10;
LL submax[maxn];
LL ans, a[maxn];
LL delta,sum[maxn];

int main(){
    LL n, d;
    scanf("%lld %lld", &n,&d);
    for(LL i=1; i<=n; i++) scanf("%lld", &a[i]), sum[i]=sum[i-1]+a[i];
    submax[n] = sum[n];
    for(LL i=n-1; i>=1; i--) submax[i] = max(sum[i], submax[i+1]);
    if(submax[1]>d) return 0*puts("-1");
    LL now = 0;
    for(LL i=1; i<=n; i++){
        if(a[i]) now += a[i];
        else{
            if(now>=0) continue;
            LL tt = submax[i]+delta;
            LL curadd = d-tt;
            now += curadd;
            delta += curadd;
            if(now<0) return 0*puts("-1");
            ans++;
        }
    }
    return 0*printf("%lld\n", ans);
}

E. Counting Arrays

题意:n个数拆成k个可以重复的数的乘积方案数

解法:对每个因子单独考虑,就会发现对于每一个质数因子来说,假设有x个,就相当于把x个数放在k个没有标号的桶里面,这个组合数是C(k+x-1,x),最后还有考虑负数的情况i昂,就是C(2,n)+(4,n)+...+C(n, n)这样求和之后答案就是2^(n-1),乘起来就可以了。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 7;
LL inv[maxn];
LL prime[maxn];
bool isprime[maxn];
int cnt = 0;
void init()
{
    for(int i=1; i<maxn; i++) isprime[i] = 1;
    for (LL i = 2; i < maxn; i++)
    {
        if (isprime[i])
        {
            for (LL j = i + i; j < maxn; j += i) isprime[j] = 0;
        }
    }
    for (int i = 2; i < maxn; i++) if (isprime[i]) prime[++cnt] = i;
}
vector <int> Get(int x)
{
    vector <int> ret;
    for (int i = 1; prime[i]*prime[i] <= x; i++)
    {
        int t = 0;
        while (x % prime[i] == 0)
        {
            t++;
            x /= prime[i];
        }
        if (t) ret.push_back(t);
    }
    if (x > 1) ret.push_back(1);
    return ret;
}
LL qsm(LL a, LL n)
{
    LL ret = 1;
    while (n)
    {
        if (n & 1) ret = ret * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return ret;
}
LL C(LL n, LL m)
{
    m = n - m;
    LL ret = 1;
    for (LL i = 1; i <= m; i++) ret = ret * (n - i + 1) % mod * inv[i] % mod;
    return ret;
}
void predeal()
{
    init();
    inv[0] = inv[1] = 1;
    for (int i = 2; i < maxn; i++)
    {
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
}
int main()
{
    predeal();
    int q, x, y;
    scanf("%d", &q);
    while(q--){
        scanf("%d %d", &x,&y);
        vector <int> now = Get(x);
        LL ans = 1;
        for(int v:now){
            ans = ans * C(y+v-1, y-1) % mod;
        }
        ans = ans * qsm(2, y-1) % mod;
        if(ans < 0) ans += mod;
        printf("%lld\n", ans);
    }
    return 0;
}

F. Subtree Minimum Query

题意:一个有根树,每次查询x的祖先节点和x节点的距离小于等于y的所有点的值的最小值。

解法:dfs,按照深度动态建树,启发式合并即可。空间复杂度O(nlogn),时间复杂度O(nlogn)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
const int maxm = maxn*40;
const int inf = 0x3f3f3f3f;
int a[maxn], dep[maxn];
vector <int> G[maxn];
int T[maxn], tot;
int val[maxm], ls[maxm], rs[maxm];
void push_up(int rt){
    val[rt] = min(val[ls[rt]], val[rs[rt]]);
}
void update(int pos, int v, int l, int r, int &rt){
    rt = ++tot;
    if(l==r){
        val[rt] = v;
        return;
    }
    int mid = (l+r)/2;
    if(pos <= mid) update(pos, v, l, mid, ls[rt]);
    else update(pos, v, mid+1, r, rs[rt]);
    push_up(rt);
}
int merge(int u, int v){
    if(!u) return v;
    if(!v) return u;
    int t = ++tot;
    ls[t] = merge(ls[u], ls[v]);
    rs[t] = merge(rs[u], rs[v]);
    if(ls[t]||rs[t]) push_up(t);
    else val[t] = min(val[u], val[v]);
    return t;
}
int query(int L, int R, int l, int r, int rt){
    if(!rt) return inf;
    if(L<=l&&r<=R) return val[rt];
    int mid = (l+r)/2;
    if(R<=mid) return query(L, R, l, mid, ls[rt]);
    else if(L>mid) return query(L, R, mid+1, r, rs[rt]);
    else return min(query(L,mid,l,mid,ls[rt]), query(mid+1,R,mid+1,r,rs[rt]));
}
void dfs(int u, int f){
    update(dep[u], a[u], 1, maxn-1, T[u]);
    for(int v:G[u]){
        if(v==f) continue;
        dep[v] = dep[u]+1;
        dfs(v, u);
        T[u] = merge(T[u], T[v]);
    }
}
int main(){
    val[0] = inf;
    int n, r;
    scanf("%d%d", &n,&r);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    for(int i=1; i<n; i++){
        int u, v;
        scanf("%d%d", &u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dep[r] = 1;
    dfs(r, 0);
    int m, last = 0;
    scanf("%d", &m);
    for(int i=1; i<=m; i++){
        int p, q;
        scanf("%d%d", &p,&q);
        int x = (p+last)%n+1, y = (q+last)%n;
        printf("%d\n", last = query(dep[x], min(maxn-1, dep[x]+y), 1, maxn-1, T[x]));
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值