2020牛客寒假算法基础集训营2

这一生的挚爱 提交于 2020-02-07 00:44:34

我这么写会不会侵权...毕竟这是收钱的比赛

F.拿物品

题解

一开始理解错了,以为每个人只是取当前最大的就行,后来发现不对,我取了这个物品,获得\(a_i\) ,不取走获得\(-b_i\) ,所以就按照\(a+b\) 排序然后按大小选就行

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
const int maxn=200010;
int n,a[maxn],b[maxn],has[maxn],A[maxn],B[maxn],la,lb;
PII c[maxn];
int main()
{
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)b[i]=read(),c[i]=make_pair(a[i]+b[i],i);
    sort(c+1,c+n+1);
    for(int i=1;i<=n;i++)
    {
        if(i%2)A[++la]=c[n-i+1].Y;
        else B[++lb]=c[n-i+1].Y;
    }
    for(int i=1;i<la;i++)printf("%d ",A[i]);
    printf("%d\n",A[la]);
    for(int i=1;i<lb;i++)printf("%d ",B[i]);
    printf("%d\n",B[lb]);
    return 0;
}

I.建通道

题解

这场比赛最有价值的一道,题意是建一棵两两边权分别为\(lowbit(v_i⊕v_j)\) 的最小生成树
根据套路,肯定先上\(Trie\) ,我们发现两点边权就是他们在\(Trie\) 树上的LCA所对应的\(lowbit\) ,为了使得最小,这个LCA肯定越小越好。
我们求出这一坨数的总LCA,(这里的LCA就对应着这些二进数的逆序的LCP)(这个LCA显然把所有数划分成了两个集合(一左一右) 我们一定用通过一种连边方式使得这个二分图联通方式是一棵树,所以答案就是\((n-1)*lowbit(LCA)\) .
一定要注意去重。(忘记去重的我当时怀疑人生)
听说CF上还有一个异或最小生成树,明天做做去。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
const int maxn=200010;
int n,v[maxn],m;
int lca(int u,int v)
{
    int res=0,t=u;
    while(u && v)
    {
        if((u&1)==(v&1))res++;
        else return (t&((1<<res)-1));
        u>>=1;v>>=1;
    }
    return (t&((1<<res)-1));
}
int main()
{
    n=read();
    for(int i=0;i<n;i++)v[i]=read();
    sort(v,v+n);
    n=unique(v,v+n)-v;
    int now=v[0];
    for(int i=1;i<n;i++)now=lca(now,v[i]);
    int len=0;
    while(now)len++,now>>=1;
    printf("%lld\n",(LL)(n-1)*(LL)(1<<len));
    return 0;
}

J.求函数

题解

看着就像线段树,推一推式子发现满足区间加法,那就直接上了。

#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<LL,LL> PII;
#define X first
#define Y second
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
const int MOD=1000000007,maxn=200010;
int n,m,A[maxn],B[maxn],kk,bb,tp,x;
LL k[maxn<<2],b[maxn<<2];
void maintain(int L,int R,int o)
{
    int mid=L+R>>1,lo=o<<1,ro=lo|1;
    k[o]=(k[lo]*k[ro])%MOD;
    b[o]=(k[ro]*b[lo]+b[ro])%MOD;
} 
void build(int L,int R,int o)
{
    if(L==R){
        k[o]=A[L];b[o]=B[L];
        return;
    }
    int mid=L+R>>1,lo=o<<1,ro=lo|1;
    build(L,mid,lo);build(mid+1,R,ro);
    maintain(L,R,o);
}
void update(int L,int R,int o,int x)
{
    if(L==R){
        k[o]=kk;b[o]=bb;
        return; 
    }
    int mid=L+R>>1,lo=o<<1,ro=lo|1;
    if(x<=mid)update(L,mid,lo,x);
    else update(mid+1,R,ro,x);
    maintain(L,R,o);
} 
PII query(int L,int R,int o,int ql,int qr)
{
    if(L==ql && R==qr)return make_pair(k[o],b[o]); 
    int mid=L+R>>1,lo=o<<1,ro=lo|1;
    if(qr<=mid)return query(L,mid,lo,ql,qr);
    else if(ql>mid)return query(mid+1,R,ro,ql,qr);
    PII t1=query(L,mid,lo,ql,mid),t2=query(mid+1,R,ro,mid+1,qr);
    return make_pair((t1.X*t2.X)%MOD,(t2.X*t1.Y+t2.Y)%MOD);
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)A[i]=read();
    for(int i=1;i<=n;i++)B[i]=read();
    build(1,n,1);
    while(m--)
    {
        tp=read();
        if(tp==1)
        {
            x=read();kk=read();bb=read();
            update(1,n,1,x);
        }
        else
        {
            kk=read();bb=read();
            PII t=query(1,n,1,kk,bb);
            printf("%lld\n",(t.X+t.Y)%MOD);
        }
    }
    return 0;
}

废话

老子以后干什么都开long long[微笑]

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!