牛客练习赛7 题解

A:

 在Alice和Bob面前的是两个骰子,上面分别写了六个数字。

 Alice和Bob轮流丢掷骰子,Alice选择第一个骰子,而Bob选择第二个,如果谁投掷出的数更大,谁就可以获胜。

  现在给定这两个骰子上的6个数字,你需要回答是Alice获胜几率更大,还是Bob获胜几率更大。(请注意获胜几率相同的情况)

解法:水题,模拟一下。

#include 
using namespace std;
int a[10], b[10];
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
    int ans1=0,ans2=0;
    for(int i=1; i<=6; i++) scanf("%d", &a[i]);
    for(int i=1; i<=6; i++) scanf("%d", &b[i]);
    for(int i=1; i<=6; i++)
        for(int j=1; j<=6; j++)
    {
        if(a[i]>b[j]) ans1++;
        if(a[i]ans2) puts("Alice");
    else if(ans1

B:

在遥远的东方,有一家糖果专卖店。
这家糖果店将会在每天出售一些糖果,它每天都会生产出m个糖果,第i天的第j个糖果价格为C[i][j]元。
现在的你想要在接下来的n天去糖果店进行选购,你每天可以买多个糖果,也可以选择不买糖果,但是最多买m个。(因为最多只生产m个)买来糖果以后,你可以选择吃掉糖果或者留着之后再吃。糖果不会过期,你需要保证这n天中每天你都能吃到至少一个糖果。
这家店的老板看你经常去光顾这家店,感到非常生气。(因为他不能好好睡觉了)于是他会额外的要求你支付点钱。具体来说,你在某一天购买了 k 个糖果,那么你在这一天需要额外支付 k 2 的费用。

那么问题来了,你最少需要多少钱才能达成自己的目的呢?

解法:贪心。

#include 
using namespace std;
typedef long long LL;
const int maxn = 1e6+7;
int n, m, S[maxn], top, cnt, edgecnt, dfsclk, head[maxn], id[maxn], a[maxn], fa[maxn], FA[maxn], num[maxn], low[maxn], dfn[maxn];
bool vis[maxn];
LL all[maxn], now[maxn];
struct edge{
    int to,next;
}E[maxn*2];
void init(){
    memset(head,-1,sizeof(head));
    edgecnt=0;
}
void add(int u,int v){
    E[edgecnt].to=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
void Tarjan(int x){
    low[x]=dfn[x]=++dfsclk;
    S[++top]=x;vis[x]=1;
    for(int i=head[x];~i;i=E[i].next){
        int v=E[i].to;
        if(!dfn[v]){
            Tarjan(v);
            low[x]=min(low[x],low[v]);
        }else if(vis[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(low[x]==dfn[x]){
        cnt++;
        while(1){
            int Now=S[top--];
            vis[Now]=0;
            id[Now]=cnt;
            all[cnt]+=a[Now];
            num[cnt]++;
            if(Now==x) break;
        }
    }
}
bool check(LL x){
    LL sum=0;
    for(int i=1; i<=cnt; i++) now[i]=all[i];
    for(int i=1; i<=cnt; i++){
        if(now[i]m) return false;
    }
    return true;
}
int main(){
    scanf("%d%d",&n,&m);
    init();
    for(int i=1; i<=n; i++){
        scanf("%d", &fa[i]);
        if(fa[i]!=-1&&fa[i]!=i){
            add(fa[i],i);
        }
    }
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
    for(int i=1;i<=cnt;i++) FA[i]=-1;
    for(int i=1;i<=n;i++) if(fa[i]!=-1&&id[fa[i]]!=id[i]) FA[id[i]] = id[fa[i]];
    LL L=0,R=1000000000,ans=0;
    while(L<=R){
        LL mid=L+R>>1;
        if(check(mid)) ans=mid,L=mid+1;
        else R=mid-1;
    }
    return 0*printf("%lld\n", ans);
}

C:

平日里写hash的时候,总有某些选手由于脸黑而导致惨遭卡模数,然后一些恶意卡模数的出题人也因此身败名裂。为了防止被卡,我们用一种高级的随机方式来代替原来的线性随机生成,也就是所谓的随机树!

现在有一棵编号为0~n-1的有根树,其中0是树的根。每个节点初始有一个值Ti。现在要求支持一下两种操作:

1.  给出两个正整数u和x,我们将Tu的值乘以x,我们将这种操作称为SEED操作。

2.  给出一个正整数i,询问Si以及它一共有多少个正约数。其中Si表示以i为根的子树所有点的权值的乘积,我们将这种操作称为RAND操作。

容易发现,这样得到的答案还是很随机的。(其实不是)

你需要回答每一次的询问,由于一个数的约数个数可能非常多,这个数也可以非常大,你只需要把答案对1e9+7取模就可以了。

解法:

牛客练习赛7 题解_第1张图片

#include 
using namespace std;
typedef long long LL;
const int maxn = 100010;
const int mod = 1e9+7;
int n, p[6][maxn];
inline int lowbit(int x){return x&(-x);}
void add(int x, int y, int z){
    while(y<=n){
        p[x][y]+=z;
        y+=lowbit(y);
    }
}
int query(int x, int y){
    int ret = 0;
    while(y){
        ret += p[x][y];
        y-=lowbit(y);
    }
    return ret;
}
int getsum(int x, int l, int r){
    return query(x,r) - query(x, l-1);
}
int a[maxn], L[maxn], R[maxn], dfsclk;
vector  G[maxn];
int pri[] = {2, 3, 5, 7, 11, 13};
void dfs(int u, int pre){
    L[u] = ++dfsclk;
    for(int i=0; i<6; i++){
        int num = 0;
        while(a[u]%pri[i]==0){
            num++;
            a[u]/=pri[i];
        }
        add(i,L[u],num);
    }
    for(int i=0; i>=1;
    }
    return ret;
}
 
int main(){
    scanf("%d",&n);
    for(int i=1; i

D:
珂朵莉给了你一个无向图,每次查询给t个点以及一个常数s,求有多少个图中的点距离给出的那t个点中至少一个距离 <= s

解法:每次把一堆点拿进队列BFS

#include 
using namespace std;
const int maxn = 5010;
bool vis[maxn][maxn];
struct Edge
{
    int to, next;
} E[maxn * maxn];
int head[maxn], edgecnt;
bool mark[maxn];
void init()
{
    memset(head, -1, sizeof(head));
    edgecnt = 0;
}
void add(int u, int v)
{
    E[edgecnt].to = v, E[edgecnt].next = head[u], head[u] = edgecnt++;
}
queue  >q;
int bfs(int r)
{
    int ret = 0;
    while (!q.empty())
    {
        pair  now = q.front();
        q.pop();
        int u = now.first, d = now.second;
        if (d <= r) ret++;
        if (d > r) continue;
        for (int i = head[u]; ~i; i = E[i].next)
        {
            int v = E[i].to;
            if (mark[v] == false)
            {
                q.push(make_pair(v, d + 1));
                mark[v] = true;
            }
        }
    }
    return ret;
}
 
int main()
{
    int n, m, T;
    scanf("%d%d%d", &n, &m, &T);
    init();
    while (m--)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        if (vis[u][v] == false)
        {
            add(u, v);
            add(v, u);
            vis[u][v] = vis[v][u] = true;
        }
    }
    int t, s, u;
    while (T--)
    {
        scanf("%d%d", &t, &s);
        memset(mark, false, sizeof(mark));
        while (t--)
        {
            scanf("%d", &u);
            if (!mark[u])
            {
                q.push(make_pair(u, 0));
                mark[u] = 1;
            }
        }
        int ans = bfs(s);
        printf("%d\n", ans);
    }
}

E:

珂朵莉给了你一个序列,有n*(n+1)/2个子区间,求出她们各自的逆序对个数,然后加起来输出

解法:

10分做法:puts(“0” );

30分做法:直接暴力

60分做法:优化暴力

70分做法:

考虑计算贡献

如果有i < j , a[i] > a[j]的点对( i , j )

则会在1 <= l <= i , j <= r <= n的子区间[l,r]中出现

则对答案的贡献是i * ( n - j + 1 )

所以用树状数组维护或者分治(归并排序)即可

做法和求普通逆序对差不多

80->100分做法:

发现答案是O( n^4 )级别

冷静分析出题人会不会卡爆long long

发现随机数据答案是O( n^4 )级别

放弃吧,还是写高精度吧

根据常数不等,80->100

#include 
using namespace std;
typedef long long LL;
const int maxn = 1000010;
struct BigInt
{
    int a[105];
    int len;
    BigInt()
    {
        memset(a, 0, sizeof(a));
        len = 1;
    }
    void add(LL b)
    {
        int t[105] = {0};
        int L = 0;
        while (b)
        {
            t[++L] = b % 10;
            b /= 10;
        }
        len = max(len, L);
        for (int i = 1; i <= len; i++) a[i] += t[i];
        for (int i = 1; i <= len; i++)
        {
            a[i + 1] += a[i] / 10;
            a[i] %= 10;
        }
        if (a[len + 1]) len++;
    }
    void Print()
    {
        for (int i = len; i >= 1; i--)
        {
            printf("%d", a[i]);
        }
        printf("\n");
    }
};
int n, cnt = 1, a[maxn];
pair  t[maxn];
LL c[maxn];
inline int lowbit(int x)
{
    return x & -x;
}
void add(int pos, int v)
{
    while (pos > 0)
    {
        c[pos] += v;
        pos -= lowbit(pos);
    }
}
LL query(int pos)
{
    LL ret = 0;
    while (pos <= cnt)
    {
        ret += c[pos];
        pos += lowbit(pos);
    }
    return ret;
}
 
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        t[i] = make_pair(a[i], i);
    }
    sort(t + 1, t + n + 1);
    for (int i = 1; i <= n; i++)
    {
        if (t[i - 1].first != t[i].first) cnt++;
        a[t[i].second] = cnt;
    }
    BigInt ans;
    for (int i = 1; i <= n; i++)
    {
                add(a[i], i);
        ans.add(query(a[i] + 1) * 1LL * (n - i + 1));
 
    }
    ans.Print();
}

F:

有n种大佬,第i种大佬有ai
珂朵莉想让最少个数的一种大佬的个数最多
你可以创造m个任意种类的大佬,并且可以把一些大佬变成另一些大佬
x -> y意味着可以把任意个x类型的大佬变成y类型的大佬
一个大佬可以被转换多次
对于每个y,最多有一个x使得x -> y成立

解法:

牛客练习赛7 题解_第2张图片

#include 
using namespace std;
typedef long long LL;
const int maxn = 1e6+7;
int n, m, S[maxn], top, cnt, edgecnt, dfsclk, head[maxn], id[maxn], a[maxn], fa[maxn], FA[maxn], num[maxn], low[maxn], dfn[maxn];
bool vis[maxn];
LL all[maxn], now[maxn];
struct edge{
    int to,next;
}E[maxn*2];
void init(){
    memset(head,-1,sizeof(head));
    edgecnt=0;
}
void add(int u,int v){
    E[edgecnt].to=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
void Tarjan(int x){
    low[x]=dfn[x]=++dfsclk;
    S[++top]=x;vis[x]=1;
    for(int i=head[x];~i;i=E[i].next){
        int v=E[i].to;
        if(!dfn[v]){
            Tarjan(v);
            low[x]=min(low[x],low[v]);
        }else if(vis[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(low[x]==dfn[x]){
        cnt++;
        while(1){
            int Now=S[top--];
            vis[Now]=0;
            id[Now]=cnt;
            all[cnt]+=a[Now];
            num[cnt]++;
            if(Now==x) break;
        }
    }
}
bool check(LL x){
    LL sum=0;
    for(int i=1; i<=cnt; i++) now[i]=all[i];
    for(int i=1; i<=cnt; i++){
        if(now[i]m) return false;
    }
    return true;
}
int main(){
    scanf("%d%d",&n,&m);
    init();
    for(int i=1; i<=n; i++){
        scanf("%d", &fa[i]);
        if(fa[i]!=-1&&fa[i]!=i){
            add(fa[i],i);
        }
    }
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
    for(int i=1;i<=cnt;i++) FA[i]=-1;
    for(int i=1;i<=n;i++) if(fa[i]!=-1&&id[fa[i]]!=id[i]) FA[id[i]] = id[fa[i]];
    LL L=0,R=1000000000,ans=0;
    while(L<=R){
        LL mid=L+R>>1;
        if(check(mid)) ans=mid,L=mid+1;
        else R=mid-1;
    }
    return 0*printf("%lld\n", ans);
}


你可能感兴趣的:(ACM/ICPC,Wanna_fly)