2018 nowcoder 多校 6

补题进度 [6/10] 场上3题
全程梦游……
D Bulbasaur
这道题直接找body对应的最大的边就行,因为一个head可以被多次选取。

A Singing Contest
这道题直接贪心一下,因为一轮输了之后直接被淘汰掉了,就不存在田忌赛马之类的优化了,所以每次只要找到第一个比对手大的就行。

J Heritage of skywalkert
题意是随机生成1e7个数,求出任意两个不相同的数之间最大的LCM。
首先随机生成两个数,这两个数互质的概率是6/(pi^2),所以我们取前100大的数,暴力下就行。
这道题纯属就是看你敢不敢暴力,会不会分析……

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
using namespace std;
#define uint unsigned
#define ull unsigned long long
const int maxn = 1e7 + 1;
const uint INF = 0x3f3f3f3f;
uint tang(uint &x,uint &y,uint &z)
{
    uint t;
    x ^= x << 16;
    x ^= x >> 5;
    x ^= x << 1;
    t = x;
    x = y;
    y = z;
    z = t ^ x ^ y;
    return z;
}
uint num[maxn];
int main()
{

    int ca,cat = 1;
    scanf("%d",&ca);
    while(ca--)
    {
        printf("Case #%d: ",cat++);
        int n;
        uint a,b,c;
        scanf("%d%u%u%u",&n,&a,&b,&c);
        for(int i = 0;i<n;i++)
        {
            num[i] = tang(a,b,c);
        }
        int m = min(100,n);
        if(n > 100)
        {
            nth_element(num,num+100,num+n,[](uint x,uint y){return x > y;});
        }
        ull ans = 0;
        for(int i = 0;i<m;i++)
        {
            //printf("%u ",num[i]);
            for(int j = 0;j<m;j++)
            {
                if(i == j) continue;
                ans = max(ans,(ull)num[i]/__gcd(num[i],num[j])*num[j]);
                //cout<<num[i]/__gcd(num[i],num[j])*num[j]<<endl;
            }
        }
        printf("%llu\n",ans);

    }

    // printf("%I64u\n",(ull)num[0]*num[1]/__gcd(num[0],num[1]));
    return 0;
}

C Generation I
题意是:有N个集合(不重复),每次想I到N每个集合添加一个相同的数,i从1到变化。
首先分析一下每个集合不同的情况,然后会发现每个不同的数字,都只和第一次天际的位置有关系,我们把数字当成不同颜色的小球,每个集合当成箱子。
那么我们把问题转化一下,相当于是K个颜色的小球放进N个箱子,每个箱子能放多个,颜色的放法和箱子的放法分开,就会的到下面这个式子

min(n1,m)k=1n!(nk)!(n1k1) ∑ m i n ( n − 1 , m ) k = 1 n ! ( n − k ) ! ∗ ( n − 1 k − 1 )

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e6+6;
const ll mod =998244353;
ll n,m;
ll f[maxn],df[maxn],inv[maxn];
int main()
{
    inv[1] = 1;
    for(int i = 2;i<maxn;i++)
    {
        inv[i] = 1LL*(mod - mod/i)*inv[mod%i]%mod;
    }

    int ca,cat = 1;
    scanf("%d",&ca);
    while(ca--)
    {
        scanf("%lld%lld",&n,&m);
        f[0] = df[0] = 1;
        for(int i = 1;i<=min(n,m);i++) f[i] = 1LL*f[i-1]*((n - i)%mod)%mod*inv[i]%mod;
        for(int i = 1;i<=min(n,m);i++) df[i] = 1LL*df[i-1]*((m +1 - i)%mod)%mod;
        ll ans = 0;
        for(int i = 1;i<=min(n,m);i++)
        {
            ans = (ans + 1LL*f[i-1]*df[i])% mod;
        }
        printf("Case #%d: %lld\n",cat++,ans);
    }
    return 0;
}

I Team Rocket
题意是问某个查询的区间删除时删除了多少的节点,然后输出所有节点被删除的时间(第几次查询)
线段树可搞,在每个每个节点上挂一个vector,记录那些节点在这个树节点对应的区间内。删除的时候直接删除,要注意重复删除的问题。还有就是数据最好离散化一下。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 4e5+5; // 1e6;
const int MAXM = 1<<21;
int T,n,m;
int l[MAXN],r[MAXN], des[MAXN], ans[MAXN],hs[MAXN];
int dd[MAXN<<1];
int tot=0;

const int mod = 998244353;
vector<int> STree[MAXM];
#define lson(k) (k<<1)
#define rson(k) (k<<1|1)
#define pb push_back
#define rep(i,a,b) for (int i=(a);i<(b);i++)
void build(int k, int l, int r)
{
    STree[k].clear();
    if (r-l<=1)
    {
        return;
    }
    int m=(l+r)>>1;
    build(lson(k), l, m);
    build(rson(k), m, r);
    return;
}
void modify(int k, int l, int r, int ql, int qr, int val)
{
    if (r<=ql||qr<=l) return;
    if (ql<=l&&r<=qr)
    {
        STree[k].pb(val);
        return;
    }
    int m=(l+r)>>1;
    modify(lson(k),l,m,ql,qr,val);
    modify(rson(k),m,r,ql,qr,val);
    return;
}
int cnt = 0;
int t[MAXN];
void destroy(int k, int l, int r, int q)
{
    if (r<=q||q<l) return;
    if (r-l<=1)
    {
        if (l==q)
        {
            for (auto x : STree[k])
            {
                if (!hs[x]) t[cnt++]=x, hs[x]=1;
            }
            STree[k].clear();
        }
        return;
    }
    int m = (l+r)>>1;
    destroy(lson(k),l,m,q);
    destroy(rson(k),m,r,q);
    for (auto x : STree[k]) if (!hs[x]) t[cnt++]=x, hs[x]=1;
    STree[k].clear();
        return;

}
int main()
{
    // freopen("in.txt","r",stdin);
    scanf("%d",&T);
    // de(T)
    rep(cas, 1, T+1)
    {
        // scanf("%d%d",&n,&m);
        tot=0;
        scanf("%d%d", &n,&m);
        // de(n)  de(m)
        rep(i, 1, n+1)
        {
            //read(l[i]); read(r[i]);
             scanf("%d%d",&l[i],&r[i]);
            // de(l[i]) de(r[i])
            r[i]++;
            dd[tot++]=l[i];
            dd[tot++]=r[i];
            ans[i]=0;
            hs[i]=0;
        }
        sort(dd,dd+tot);
        tot = unique(dd,dd+tot)-dd;
        build(1,0,tot);
        rep(i,1,n+1)
        {
            l[i]=lower_bound(dd,dd+tot,l[i])-dd;
            r[i]=lower_bound(dd,dd+tot,r[i])-dd;
            //printf("%d %d\n",l[i],r[i]);
            modify(1,0,tot,l[i],r[i],i);
        }
        int q, res = 0;
        printf("Case #%d:\n",cas);
        rep(ii,1,m+1)
        {
            //read(q);
             scanf("%d",&q);
            int des = res^q;
            cnt = 0;
            des = upper_bound(dd,dd+tot,des)-dd-1;
            destroy(1,0,tot,des);
            printf("%d\n",cnt);
            res = 1;
            rep(i,0,cnt)
            {
                res=1ll*res*t[i]%mod;
                // modify(1,0,tot,l[t[i]],r[t[i]],t[i],1);
                ans[t[i]]=ii;
            }
            if (cnt==0) res = 0;
            cnt=0;
        }
        rep(i,1,n+1) printf("%d%c",ans[i]," \n"[i==n]);
    }

    return 0;
}

G Pikachu
求一个树上每对结点的最大流之和……居然不取模
首先我们先想到最大流最小割定理,树上求一个最小割,想见一定要把除了源点或者除了汇点的边都排除掉,那么这个问题就成了树上任意两点间的间距。
然后我们用三个dfs解决这个问题
dfs1:更新出节点的深度和子树的大小,子树之外节点的多少
中间插一步,根据深度和边,更新出来出来每个点向根的点权。
dfs2:更新出每个节点向树根的权值
dfs3:一个点的权值和等于这个点都树根的权值加上父节点的出自己以外的子节点的权值,加上子节点以外点的权值……
最后排个序,越小的比较深,所以每次计算的时候会被计算多次。

#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
#include <algorithm>
#include <set>
#include <unordered_map>
#include <map>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
vector<int> edge[MAXN];
ll rd[MAXN],totd[MAXN],sz[MAXN],rsz[MAXN];
ll we[MAXN];
ll depth[MAXN];
int n,m;
void dfs1(int u,int p,int dep)
{
    sz[u] = 1;
    depth[u] = dep;
    for(auto v : edge[u])
    {
        if(v == p)
            continue;
        dfs1(v,u,dep+1);
        sz[u] += sz[v];
    }
    rsz[u] = n - sz[u];
}

void dfs2(int u,int p)
{
    rd[u] = 0;
    for(auto v : edge[u])
    {
        if(v == p)
            continue;
        dfs2(v,u);
        rd[u] += rd[v] + sz[v] * we[v];
    }
}
void solve(int u,int p,ll add)
{
    totd[u] = rd[u] + add;
    ll sum = add;
    for(auto v : edge[u])
    {
        if(v == p)
            continue;
        sum += rd[v] + sz[v] * we[v];
    }
    for(auto v : edge[u])
    {
        if(v == p)
            continue;
        ll tmp = sum - rd[v] - sz[v]*we[v];
        solve(v,u,tmp + rsz[v]*we[v]);
    }
}
vector<tuple<int,int,int> > tp;
int main()
{

    int ca,cat = 1;
    scanf("%d",&ca);
    while(ca--)
    {
        scanf("%d",&n);
        tp.clear();
        for(int i = 1;i<=n;i++) we[i] = 0,edge[i].clear();
        for(int i = 0; i<n-1; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            edge[u].push_back(v);
            edge[v].push_back(u);
            tp.push_back(make_tuple(u,v,w));
        }
        dfs1(1,0,0);
        for(auto &tu : tp)
        {
            int u,v,w;
            tie(u,v,w) = tu;
            if(depth[u] < depth[v])
                we[v] = w;
            else
                we[u] = w;
        }
        dfs2(1,0);
        solve(1,0,0);
        __int128 ans = 0;

        sort(totd+1,totd + n+1);
        for(int i = 1; i<=n; i++)
        {
            ans += totd[i] *(n - i);
        }
        const __int128 LIM = 1000000000000000000;
        if(ans >= LIM)
            printf("Case #%d: %lld%018lld\n",cat++,ll(ans/LIM),ll(ans%LIM));
        else
            printf("Case #%d: %lld\n",cat++,ans);

    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值