• 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
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    (转)下拉刷新组合控件的制作小结
    ActivityGroup的子activity响应back事件的顺序问题
    用CSS实现竖向圆角效果的折叠菜单代码
    蓝色风格的JS+CSS个性菜单代码
    用CSS实现WinVista风格的菜单代码
    div+css+js打造的一款菜单的收缩与展开代码
    五颜六色的弹性下拉导航菜单代码
    【荐】鼠标放上弹出下拉菜单的代码
    网页顶部隐藏css菜单代码
    【教程】简单滑动门代码
  • 原文地址:https://www.cnblogs.com/cjyyb/p/8468067.html
Copyright © 2020-2023  润新知