Codeforces Round #466 (Div. 2)

所有的题目都可以在CodeForces上查看

中间看起来有很多场比赛我没有写了
其实是因为有题目没改完
因为我不想改,所以就没有写了(大部分题目还是改完了的)
我还是觉得如果是打了的比赛就一场一场写比较好
要不然以后就写有难度的、比较好的题目??



这场比赛时间真心良心(只是没吃饭)
状态也很好,考场上把\(ABCDE\)切了
\(F\)是自己弃疗了。。。

不管啦不管啦,进入正题了

题解

A.Points on the line

翻译:

给定一个长度为\(n\)的数组
问最少删去几个数之后使得最大值减最小值的结果小于等于\(d\)

题解

考虑\(n\)的范围很小
排序之后枚举每一段区间,计算答案即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int a[200],n,D;
int main()
{
    n=read();D=read();
    for(int i=1;i<=n;++i)a[i]=read();
    sort(&a[1],&a[n+1]);
    int ans=0;
    for(int i=n;i;--i)
    {
        ans++;
        for(int j=1;j+i-1<=n;++j)
            if(a[j+i-1]-a[j]<=D)
            {
                cout<<ans-1<<endl;
                return 0;
            }
    }

}

B.Our Tanya is Crying Out Loud

翻译

给定\(n,k,A,B\),
一开始\(x=n\),
每次可以花费$A \(的代价 使得\)x=x-1$ ;
每次可以花费\(B\)的代价
使得\(x=x/k\)(前提是\(x\%k=0\))
问使得\(x\)变成\(0\)的最小代价

题解

每次贪心的考虑把\(x\)变成\([\frac{x}{k}]\)的代价
看看是一个个减更优还是先减再除更优

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
ll n,K,A,B;
int main()
{
    n=read();K=read();A=read();B=read();
    ll ans=0;
    if(n<K){ans=A*(n-1);cout<<ans<<endl;return 0;}
    if(K==1){ans=A*(n-1);cout<<ans<<endl;return 0;}
    while(n!=1)
    {
        if(n<K)
        {
            ans+=A*(n-1);
            cout<<ans<<endl;
            return 0;
        }
        ll gg=n/K;
        ll t1=(n-gg)*A;
        ll t2=(n%K)*A+B;
        ans+=min(t1,t2);
        n=gg;
    }
    cout<<ans<<endl;
    return 0;
}

C.Phone Numbers

翻译

给定一个长度为\(n\)的串\(S\)
要求只能用\(n\)中包含的字符构建一个长度为\(k\)的字符串\(T\)
使得\(T\)的字典序最小,并且\(S\)的字典序小于\(T\)

题解

如果\(k>n\)直接把\(S\)复制一遍
后面填字典序最小的字符就行了
否则的话,把两个字符串从首位开始对齐
从\(T\)的末尾开始填
只要\(S\)的当前位置不是字典序最大的字符
就填上比它大的字符
然后前面直接复制

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
int n,k;
char ch[120000];
int ans[120000];
int g[120000];
int main()
{
    cin>>n>>k;
    scanf("%s",ch+1);
    for(int i=1;i<=n;++i)g[i]=ch[i]-96;
    sort(&g[1],&g[n+1]);
    int t=unique(&g[1],&g[n+1])-g-1;
    if(k>n)
    {
        for(int i=1;i<=n;++i)putchar(ch[i]);
        for(int i=n+1;i<=k;++i)putchar(g[1]+96);
        puts("");
        return 0;
    }
    bool fl=false;
    for(int i=k;i;--i)
    {
        int c=ch[i]-96;
        if(fl){ans[i]=c;continue;}
        if(c==g[t])ans[i]=g[1];
        else
        {
            ans[i]=g[lower_bound(&g[1],&g[t+1],c)-g+1];
            fl=true;
        }
    }
    for(int i=1;i<=k;++i)putchar(ans[i]+96);
    puts("");
    return 0;
}

D.Alena And The Heater

翻译

很难翻译呀,配着英文看还是挺好的

给定你一个数列\(A\)
以及让你构造的\(01\)串\(B\)
满足以下条件:
\(b[1]=b[2]=b[3]=b[4]=0\)
\(b[i]=1\),当满足:
\[a_i,?a_{i?-?1},?a_{i?-?2},?a_{i?-?3},?a_{i?-?4}?>r?\ and\ b_{i?-?1}?=?b_{i?-?2}?=?b_{i?-?3}?=?b_{i?-?4}?=?1\]
\(b[i]=0\),当满足:
\[a_i,?a_{i?-?1},?a_{i?-?2},?a_{i?-?3},?a_{i?-?4}?<l?\ and\ b_{i?-?1}?=?b_{i?-?2}?=?b_{i?-?3}?=?b_{i?-?4}?=?0\]
如果都不满足上面的两种情况
那么\(b[i]=b[i-1]\)
保证题目有解
输出能够构造出\(B'\)的\(l,r\)

题解

这题的关键在哪里?
保证题目有解
所以每次只需要找到\(01\)变化的地方
相应的对\(l/r\)取一个\(max/min\)
很简单吧

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 120000
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,a[MAX],b[MAX],bb[MAX];
char ch[MAX];
int p[MAX],pp[MAX];
int q[MAX];
int main()
{
    n=read();
    for(int i=1;i<=n;++i)a[i]=read();
    scanf("%s",ch+1);
    for(int i=1;i<=n;++i)bb[i]=ch[i]-48;
    for(int i=5;i<=n;++i)
    {
        p[i]=min(a[i],a[i-1]);
        p[i]=min(p[i],a[i-2]);
        p[i]=min(p[i],a[i-3]);
        p[i]=min(p[i],a[i-4]);
    }
    for(int i=5;i<=n;++i)
    {
        q[i]=max(a[i],a[i-1]);
        q[i]=max(q[i],a[i-2]);
        q[i]=max(q[i],a[i-3]);
        q[i]=max(q[i],a[i-4]);
    }

    for(int i=4;i<=n;++i)
    {
        if(b[i]==b[i-1]&&b[i]==b[i-2]&&b[i]==b[i-3])
            pp[i]=b[i];
        else pp[i]=2;
    }

    int l=-1e9,r=1e9,now=0;
    for(int i=5;i<=n;++i)
    {
        if(bb[i]!=bb[i-1])
        {
            if(bb[i]==1)l=max(l,q[i]+1);
            else r=min(r,p[i]-1);
        }
    }
    cout<<l<<' '<<r<<endl;
    return 0;
}

E. Cashback

翻译

给定长度为\(n\)的序列\(a\),以及一个\(k\)
你可以把他划分成若干段
设某一段的长度为\(len\)
那么,这一段就会删去\([\frac{len}{k}]\)个最小的数
求出这个序列能够划分出的最小和

题解

\(O(n^2)\)的\(dp\)是很显然的
\[f[i]=min(f[j]+Calc(j+1,i))\]
\(Calc\)貌似要用主席树?,
复杂度还要套个\(log\)

但是,我们真的有必要这么做吗?
仔细观察分段
如果我们分出一个长度为\(2k\)的段
似乎不会比分成两段长度为\(k\)的更优
因为两段\(k\)中是分别删去最小值
而一段\(2k\)中删去的是最小的两个值

所以,我们就可以知道分段的时候长度分成\(k\)
而不是更多的连续在一起的值
那么,分成\(k+p(p<k)\)有没有意义呢?
数字越多,最小值是单调递减的
那么,分成这样的一组不会比分出一段\(k\)更优
而分出一段\(p\)
等价于分出\(p\)个\(1\)

所以,其实我们的转移没有必要\(O(n)\)来做
只需要考虑是分出一段\(k\)
还是分出一个\(1\)就行了
每次分出一段\(k\)就要删去一个数
所以要查找区间最小值
用\(ST\)表就行了
时间复杂度\(O(nlog)\)
我因为一开始写了主席树,索性用主席树算的答案
复杂度不变,\(O(nlog)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 120000
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,a[MAX],rt[MAX],S[MAX],tt;
ll s[MAX];
ll f[MAX];
int tot,c;
struct Node
{
    int ls,rs;
    ll sum;
    int size;
}t[MAX<<5];
void Modify(int &now,int ff,int l,int r,int p,int w)
{
    now=++tot;
    t[now]=t[ff];t[now].size++;t[now].sum+=1ll*S[p]*w;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid)Modify(t[now].ls,t[ff].ls,l,mid,p,w);
    else Modify(t[now].rs,t[ff].rs,mid+1,r,p,w);
}
ll Query(int r1,int r2,int l,int r,int K)
{
    if(l==r){return K*S[l];}
    int mid=(l+r)>>1;
    int ss=t[t[r2].ls].size-t[t[r1].ls].size;
    if(K<=ss)return Query(t[r1].ls,t[r2].ls,l,mid,K);
    else return t[t[r2].ls].sum-t[t[r1].ls].sum+Query(t[r1].rs,t[r2].rs,mid+1,r,K-ss);
}
int main()
{
    n=read();c=read();
    for(int i=1;i<=n;++i)S[++tt]=a[i]=read();
    for(int i=1;i<=n;++i)s[i]=s[i-1]+a[i];
    sort(&S[1],&S[n+1]);
    tt=unique(&S[1],&S[tt+1])-S-1;
    for(int i=1;i<=n;++i)
    {
        int p=lower_bound(&S[1],&S[tt+1],a[i])-S;
        Modify(rt[i],rt[i-1],1,tt,p,1);
    }
    for(int i=1;i<c;++i)f[i]=s[i];
    for(int i=c;i<=n;++i)
    {
        f[i]=f[i-1]+a[i];
        ll gg=s[i]-s[i-c]-Query(rt[i-c],rt[i],1,tt,1);
        f[i]=min(f[i],gg+f[i-c]);
    }
    cout<<f[n]<<endl;
    return 0;
}

F. Machine Learning

翻译

给定一个长度为\(n\)的序列\(a\)
两种操作
1.询问\([l,r]\)区间内,所有数字出现次数的\(mex\)值
2.将\(p\)位置修改为\(x\)

其中\(mex\)表示的是最小的没有出现过的正整数

题解

很明显的莫队,带修改莫队
离线读入
离散之后维护每个数字出现的次数
其实没有必要对这个值进行类似于直接求\(mex\)的分块
因为出现次数在最坏情况下是\(1,2,3....\)
这个的和是平方级别的
所以\(mex\)一定是根号级别的

这样的话复杂就是对的了

但是细节很多
否则时间挂烂(比如我调了1个多小时)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 200000
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Event
{
    int id;
    int l,r,blk;
    int lst;
}Q[MAX],P[MAX];
int ans[MAX];
int S[MAX<<1],ss,n,m,a[MAX],b[MAX],blk=3000,tot,q;
int sum[MAX],Sum[MAX];
int Blk[MAX];
bool cmp(Event a,Event b){
    if(a.blk!=b.blk) return a.blk<b.blk;
    if((a.r-1)/blk!=(b.r-1)/blk)return (a.r-1)/blk<(b.r-1)/blk;
    return a.lst<b.lst;
}
void Change(int num,int w)
{
    Sum[sum[num]]--;
    if(Sum[sum[num]]==0)Blk[(Sum[sum[num]]-1)/500]--;
    sum[num]+=w;
    Sum[sum[num]]++;
    if(Sum[sum[num]]==1)Blk[(Sum[sum[num]]-1)/500]++;
}
int GetAns()
{
    for(int i=0;;++i)
    {
        if(Blk[i]==blk)continue;
        for(int j=1;j<=blk;++j)
            if(!Sum[i*blk+j])return i*blk+j;
    }
}
void Modify(int st,int now,int l,int r)
{
    if(st==now)return;
    if(now<st)
        for(int i=now+1;i<=st;++i)
        {
            a[P[i].l]=P[i].r;
            if(P[i].l>=l&&P[i].l<=r)
            {
                Change(P[i].lst,-1);
                Change(P[i].r,1);
            }
        }
    else
        for(int i=now;i>st;--i)
        {
            a[P[i].l]=P[i].lst;
            if(P[i].l>=l&&P[i].l<=r)
            {
                Change(P[i].r,-1);
                Change(P[i].lst,1);
            }
        }
}
void Work(int pos,int w)
{
    Change(a[pos],w);
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)b[i]=a[i]=read(),S[++ss]=a[i];
    for(int i=1;i<=m;++i)
    {
        int opt=read(),l=read(),r=read();
        if(opt==1){Q[++q]=(Event){q,l,r,(l-1)/blk,tot};}
        else
        {
            P[++tot]=(Event){tot,l,r,0,b[l]};
            b[l]=r;
            S[++ss]=r;
        }
    }

    sort(&S[1],&S[ss+1]);
    ss=unique(&S[1],&S[ss+1])-S-1;
    for(int i=1;i<=n;++i)a[i]=lower_bound(&S[1],&S[ss+1],a[i])-S;
    for(int i=1;i<=tot;++i)
    {
        P[i].r=lower_bound(&S[1],&S[ss+1],P[i].r)-S;
        P[i].lst=lower_bound(&S[1],&S[ss+1],P[i].lst)-S;
    }
    sort(&Q[1],&Q[q+1],cmp);

    int md=0,l=1,r=0;
    for(int i=1;i<=q;++i)
    {
        while(r<Q[i].r)Work(++r,1);
        while(l>Q[i].l)Work(--l,1);
        while(r>Q[i].r)Work(r--,-1);
        while(l<Q[i].l)Work(l++,-1);
        Modify(Q[i].lst,md,l,r);
        md=Q[i].lst;
        ans[Q[i].id]=GetAns();
    }
    for(int i=1;i<=q;++i)
        printf("%d\n",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/8468067.html

时间: 2024-10-01 14:48:21

Codeforces Round #466 (Div. 2)的相关文章

Codeforces Round #466 (Div. 2) Problem A - E

从这里开始 题目列表 小结 Problem A Points on the line Problem B Our Tanya is Crying Out Loud Problem C Phone Numbers Problem D Alena And The Heater Problem E Cashback Problem F Machine Learning (Lazy Tag) 小结 这场比赛和同学一起打.本来应该是很开心的事情,结果留下好多遗憾. 第一个遗憾是没能在20分钟内消灭A.B题

小蒟蒻初次CF滚粗+爆炸记 (Codeforces Round #466 Div.2)

比赛链接:http://codeforces.com/blog/entry/57981 小蒟蒻今天初次在ZCDHJ张大佬的带领下,打了一场CF (张大佬cnblogs链接:https://www.cnblogs.com/ZCDHJ)' 英文完全看不懂,后面几题直接放弃,各位dalao请见谅 T1: 题目链接:http://codeforces.com/contest/940/problem/A 题目大意: 给你一个n个数的集合,要求你删掉若干数,其中最大的差不应该超过d,求最小删除量. (小蒟蒻

Codeforces Round #466 (Div. 2) F - Machine Learning

可以观察到,因为我们答案是通过MEX函数得到,那么假设,当前MEX的值是p,那么这时候一共有1+2+3+4...+p-1个与\(c_1\),\(c_2\),\(c_3\)...\(c_p-1\)相同,一共是p*(p-1)/2个数字,那么MEX值一定不超过\(\sqrt n\).故暴力能统计答案. 对于有修改的询问,我们采用待修改莫队,取块的大小为\(n^{\frac23}\). #include<iostream> #include<cstring> #include<alg

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿

Codeforces Round #424 (Div. 2) C. Jury Marks(乱搞)

题目链接:Codeforces Round #424 (Div. 2) C. Jury Marks 题意: 给你一个有n个数序列,现在让你确定一个x,使得x通过挨着加这个序列的每一个数能出现所有给出的k个数. 问合法的x有多少个.题目保证这k个数完全不同. 题解: 显然,要将这n个数求一下前缀和,并且排一下序,这样,能出现的数就可以表示为x+a,x+b,x+c了. 这里 x+a,x+b,x+c是递增的.这里我把这个序列叫做A序列 然后对于给出的k个数,我们也排一下序,这里我把它叫做B序列,如果我

[Codeforces] Round #352 (Div. 2)

人生不止眼前的狗血,还有远方的狗带 A题B题一如既往的丝帛题 A题题意:询问按照12345678910111213...的顺序排列下去第n(n<=10^3)个数是多少 题解:打表,输出 1 #include<bits/stdc++.h> 2 using namespace std; 3 int dig[10],A[1005]; 4 int main(){ 5 int aa=0; 6 for(int i=1;;i++){ 7 int x=i,dd=0; 8 while(x)dig[++dd

Codeforces Round #273 (Div. 2)

Codeforces Round #273 (Div. 2) 题目链接 A:签到,仅仅要推断总和是不是5的倍数就可以,注意推断0的情况 B:最大值的情况是每一个集合先放1个,剩下都丢到一个集合去,最小值是尽量平均去分 C:假如3种球从小到大是a, b, c,那么假设(a + b) 2 <= c这个比較明显答案就是a + b了.由于c肯定要剩余了,假设(a + b)2 > c的话,就肯定能构造出最优的(a + b + c) / 3,由于肯定能够先拿a和b去消除c,而且控制a和b成2倍关系或者消除

Codeforces Round #339 (Div. 2) B. Gena&#39;s Code

B. Gena's Code It's the year 4527 and the tanks game that we all know and love still exists. There also exists Great Gena's code, written in 2016. The problem this code solves is: given the number of tanks that go into the battle from each country, f