2020牛客暑假多校训练营 (第八场) E,G,I,K

E Enigmatic Partition


找规律+二阶隔项差分
我们以 a=1 m=7 打个表
在这里插入图片描述
我们发现,我们需要在一次差分过后的 10,12,14,16,18 处+1,在 15,16,17,18,19处-1。如果这样枚举差分位置会导致复杂度太大,所以我们利用二阶隔项差分解决这个问题,num=a*m,我们要在 a为增加的数组,a[num+3]++,a[num+len-1+len-2+2]–。(因为是隔了两项,所以要在20处–)。b为减少的数组,b[num+len+1]++,b[num+len-1+len-2+2]–。
最后求前缀和可以把一次差分数组求出来,再求一次前缀和把每项求出来,再求一次前缀和,之和就可以利用前缀和O(1)的查询。

#include <iostream>
#include <stdio.h>
using namespace std;
const int N=3e5+10,M=1e5;
typedef long long ll;
ll a[N],b[N],c[N];
int T,l,r,x;
int main()
{
    for(int i=3;i<=M;++i)
    for(int j=1;i*j<=M;++j)
    {
        x=i*j;
        a[x+3]++;
        a[x+i*2-1]--;
        b[x+i+1]++;
        b[x+i*2-1]--;
    }
    for(int i=3;i<=M;++i)
    a[i]+=a[i-2],b[i]+=b[i-1];
    for(int i=3;i<=M;++i)
    a[i]+=a[i-1]-b[i],c[i]=c[i-1]+a[i];
    scanf("%d",&T);
    for(int i=1;i<=T;++i)
    {
        scanf("%d %d",&l,&r);
        printf("Case #%d: %lld\n",i,c[r]-c[l-1]);
    }
    return 0;
}

G Game SET


暴力枚举,因为某些神奇的结论,如果找不到n一定小于等于20,所以我们到第21就肯定能找到,这样暴力的时间复杂度就足够了。

#include <iostream>
#include <stdio.h>
#include <set>
#include <map>
using namespace std;
const int maxn=257;
int T,n;
map<string,int>mp;
struct node
{
    int c[4];
}d[maxn];
char str[105];
bool check(int x,int y,int z)
{
    for(int i=0;i<4;++i)
    {
        set<int>st;
        int wild=0;
        if(d[x].c[i]==-1)
        wild++;
        else
        st.insert(d[x].c[i]);
        if(d[y].c[i]==-1)
        wild++;
        else
        st.insert(d[y].c[i]);
        if(d[z].c[i]==-1)
        wild++;
        else
        st.insert(d[z].c[i]);
        if(st.size()>1&&st.size()+wild<3)
        return false;
    }
    return true;
}
int main()
{
    mp["*"]=-1;
    mp["one"]=1;
    mp["two"]=2;
    mp["three"]=3;
    mp["diamond"]=1;
    mp["squiggle"]=2;
    mp["oval"]=3;
    mp["solid"]=1;
    mp["striped"]=2;
    mp["open"]=3;
    mp["red"]=1;
    mp["green"]=2;
    mp["purple"]=3;
    scanf("%d",&T);
    for(int cas=1;cas<=T;++cas)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%s",str);
            int j=1;
            for(int p=0;p<4;++p)
            {
                string a;
                for(j;str[j]!=']';++j)
                {
                    a+=str[j];
                }
                d[i].c[p]=mp[a];
                j+=2;
            }
        }
        bool flag=0;
        int i,j,k;
        for(i=1;i<=n;++i)
        {
            for(j=i+1;j<=n;++j)
            {
                for(k=j+1;k<=n;++k)
                {
                    if(check(i,j,k))
                    {
                        flag=1;
                        break;
                    }
                }
                if(flag)
                break;
            }
            if(flag)
            break;
        }
        if(!flag)
        printf("Case #%d: -1\n",cas);
        else
        {
            printf("Case #%d: %d %d %d\n",cas,i,j,k);
        }
    }
    return 0;
}

I Interesting Computer Game


我们发现,不必顺次贪心去取,因为这和次序无关,我们全局的去看,每次选只剩一个的。如果此时都不是只剩一个的话,就都可以要。我们可以对应成图,两个选项间连边,如果一个连通图是环就都可以选上,如果不是环,则会少选一个,ans–。具体利用拓扑实现即可。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=1e5+5;
int T;
int n,cnt,tot;
struct node
{
    int next,to;
}edge[maxn<<2];
int head[maxn<<1];
int ing[maxn<<1];
void add(int x,int y)
{
    edge[++tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
void init()
{
    cnt=0;
    tot=0;
    memset(head,0,sizeof(head));
    memset(ing,0,sizeof(ing));
    memset(edge,0,sizeof(edge));
}
queue<int>q;
int a[maxn],b[maxn],c[maxn<<1];
int main()
{
    scanf("%d",&T);
    for(int k=1;k<=T;++k)
    {
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%d %d",&a[i],&b[i]);
            c[++cnt]=a[i];
            c[++cnt]=b[i];
        }
        sort(c+1,c+1+cnt);
        int size=unique(c+1,c+1+cnt)-c-1;
        for(int i=1;i<=n;++i)
        {
            int aa=lower_bound(c+1,c+1+size,a[i])-c;
            int bb=lower_bound(c+1,c+1+size,b[i])-c;
            ing[aa]++;
            ing[bb]++;
            add(aa,bb);
            add(bb,aa);
        }
        int ans=size;
        for(int i=1;i<=n;++i)
        {
            int aa=lower_bound(c+1,c+1+size,a[i])-c;
            int bb=lower_bound(c+1,c+1+size,b[i])-c;
            if(ing[aa]==1&&ing[bb]==1)
            ans--;
            else
            if(ing[aa]==1)
            q.push(aa);
            else
            if(ing[bb]==1)
            q.push(bb);
        }
        while(!q.empty())
        {
            int u=q.front();
            ing[u]--;
            for(int i=head[u];i;i=edge[i].next)
            {
                int v=edge[i].to;
                if(ing[v]==1)
                ans--;
                else
                if(ing[v]>1)
                {
                    ing[v]--;
                    if(ing[v]==1)
                    q.push(v);
                }
            }
            q.pop();
        }
        printf("Case #%d: %d\n",k,ans);
    }
    return 0;
}

K Kabaleo Lite


贪心,先求一个前缀和,每次选可选的里面最大的,更新答案即可。注意会爆long long且有负数,我们可以用大数,或者__int128。

#include <bits/stdc++.h>
using namespace std;
const __int128 inf=1e5+5;
const int maxn=1e5+5;
inline __int128 read()
{
    __int128 x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
inline void write(__int128 x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
}
struct node
{
    __int128 value;
    int id;
    int pos;
}c[maxn];
int T,n;
__int128 a[maxn],b[maxn];
bool cmp(node x,node y)
{
    if(x.value==y.value)
    return x.pos>y.pos;
    else
    return x.value>y.value;
}
int main()
{
    b[0]=inf;
    scanf("%d",&T);
    for(int k=1;k<=T;++k)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            a[i]=read();
            a[i]+=a[i-1];
            c[i].value=a[i];
            c[i].id=i;
        }
        for(int i=1;i<=n;++i)
        {
            b[i]=read();
            if(b[i]<b[i-1])
            {
                c[i].pos=i;
            }
            else
            {
                b[i]=b[i-1];
                c[i].pos=c[i-1].pos;
            }
        }
        sort(c+1,c+1+n,cmp);
        __int128 ans=0;
        int last=n;
        __int128 res=0;
        for(int i=1;i<=n;++i)
        {
            if(c[i].id<=last)
            {
                last=c[i].pos-1;
                ans+=c[i].value*(b[c[i].id]-res);
                res+=b[c[i].id]-res;
            }
        }
        printf("Case #%d: %d ",k,b[1]);
        write(ans);
        printf("\n");
    }
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值