zoukankan      html  css  js  c++  java
  • 省选测试12

    总结

    仍然是三题打暴力,和前面的人分差巨大

    最近有一段时间没有 (A) 题了

    A. 灯

    分析

    首先把相同的颜色缩成一段,要求的就是总的加入的点的个数-相邻的不同颜色的点被点亮的个数

    前面的部分比较好处理,直接记录一下每一种颜色有多少个点就行了

    对于后面的部分,分情况考虑

    当某种颜色出现的次数大于 (sqrt{n})

    我们记录两个数组分别代表出现次数小于 (sqrt{n}) 的颜色对它的贡献和出现次数大于 (sqrt{n}) 的颜色对它的贡献

    前面的那一部分我们可以在更新小块信息的时候顺便维护一下

    后面的那一部分我们可以用一个二维数组预处理一下

    当某种颜色出现的次数小于 (sqrt{n})

    我们直接扫一遍它的 (vector) ,判断一下左右两边的元素能否作出贡献就行了

    复杂度为 (nsqrt{n})

    代码

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=1e5+5,maxm=355;
    int n,m,q,a[maxn],nn,blo,jud[maxn],cnt[maxn],lk[maxm][maxm],sta[maxn],tp,ans,rk[maxn],ed[maxn];
    std::vector<int> g[maxn];
    int main(){
    	n=read(),m=read(),q=read();
    	for(rg int i=1;i<=n;i++) a[i]=read();
    	rg int lst=-1,ans=0;
    	for(rg int i=1;i<=n;i++){
    		if(a[i]==lst) continue;
    		else {
    			a[++nn]=a[i];
    			lst=a[i];
    		}
    	}
    	n=nn;
    	a[n+1]=0;
    	blo=sqrt(n);
    	for(rg int i=1;i<=n;i++) g[a[i]].push_back(i);
    	for(rg int i=1;i<=m;i++) cnt[i]=g[i].size();
    	for(rg int i=1;i<=m;i++) if(cnt[i]>=blo) sta[++tp]=i,rk[i]=tp;
    	for(rg int i=2;i<=n;i++) lk[rk[a[i]]][rk[a[i-1]]]++,lk[rk[a[i-1]]][rk[a[i]]]++;
    	rg int aa,bb;
    	for(rg int i=1;i<=q;i++){
    		aa=read();
    		if(jud[aa]){
    			ans-=cnt[aa];
    			if(rk[aa]){
    				ans+=ed[rk[aa]];
    				for(rg int i=1;i<=tp;i++){
    					if(jud[sta[i]]) ans+=lk[rk[aa]][i];
    				}
    			} else {
    				for(rg int i=0;i<g[aa].size();i++){
    					bb=g[aa][i];
    					if(jud[a[bb-1]]) ans++;
    					if(jud[a[bb+1]]) ans++;
    					ed[rk[a[bb-1]]]--;
    					ed[rk[a[bb+1]]]--;
    				}
    			}
    			jud[aa]=0;
    		} else {
    			ans+=cnt[aa];
    			if(rk[aa]){
    				ans-=ed[rk[aa]];
    				for(rg int i=1;i<=tp;i++){
    					if(jud[sta[i]]) ans-=lk[rk[aa]][i];
    				}
    			} else {
    				for(rg int i=0;i<g[aa].size();i++){
    					bb=g[aa][i];
    					if(jud[a[bb-1]]) ans--;
    					if(jud[a[bb+1]]) ans--;
    					ed[rk[a[bb-1]]]++;
    					ed[rk[a[bb+1]]]++;
    				}
    			}
    			jud[aa]=1;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    B. 十字路口

    分析

    设周期为 (T)

    对于 (m<n) 的数据

    我们把每一次的观察看作点,以同一盏红绿灯作为关系连边

    设该灯在第 (i) 次观察时还有 (x_i) 秒变为绿灯,第 (i) 次观察的时间为 (t_i)

    在第 (j) 次观察时还有 (y_i) 秒 变为绿灯,第 (j) 次观察的时间为 (t_j)

    因为该灯变为绿灯的时刻肯定是相隔若干个周期的,所以 (x_i+t_i equiv x_j+t_j (mod T))

    所以如果 (x_i>x_j),我们由 (i)(j) 建一条权值为 (x_i-x_j) 的边,强制规定一下边的方向

    如果形成了环,那么肯定会有 (x_1-x_2+x_2-x_3+..-x_1 equiv 0(mod T))

    所以形成的环的长度一定是 (T) 的倍数,我们在原图上求一个最小环就是答案了

    对于 (m geq n) 的数据

    我们把每一盏灯看做点,以同一次观察作为关系连边

    设两个红绿灯红灯变绿灯的时间分别为 (t_i)(t_j),一个距离这个时刻 (x_i) 秒,一个距离这个时刻 (x_j)

    则有 (t_i-x_i equiv t_j-x_j (mod T))

    本质上还是与上面那个式子是一样的

    这样我们就可以根据 (m)(n) 的关系判断选择使用哪一种建边方式了

    时间复杂度为 (nmsqrt{nm})

    代码

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<iostream>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=1e5+5;
    int n,m,ans=0x3f3f3f3f;
    std::vector<int> g[maxn],f[maxn];
    int main(){
    	n=read(),m=read();
    	rg int aa;
    	for(rg int i=1;i<=m;i++){
    		g[i].push_back(0);
    		for(rg int j=1;j<=n;j++){
    			aa=read(),g[i].push_back(aa);
    		}
    	}
    	if(m<=n){
    		for(rg int i=1;i<=m;i++){
    			for(rg int j=0;j<=m;j++){
    				f[i].push_back(0x3f3f3f3f);
    			}
    		}
    		for(rg int k=1;k<=n;k++){
    			for(rg int i=1;i<=m;i++){
    				for(rg int j=1;j<=m;j++){
    					if(g[i][k] && g[j][k] && g[i][k]>g[j][k]){
    						f[i][j]=g[i][k]-g[j][k];
    					}
    				}
    			}
    		}
    		for(rg int k=1;k<=m;k++){
    			for(rg int i=1;i<=m;i++){
    				for(rg int j=1;j<=m;j++){
    					f[i][j]=std::min(f[i][j],f[i][k]+f[k][j]);
    				}
    			}
    		}
    		for(rg int i=1;i<=m;i++) ans=std::min(ans,f[i][i]);
    	} else {
    		for(rg int i=1;i<=n;i++){
    			for(rg int j=0;j<=n;j++){
    				f[i].push_back(0x3f3f3f3f);
    			}
    		}
    		for(rg int k=1;k<=m;k++){
    			for(rg int i=1;i<=n;i++){
    				for(rg int j=1;j<=n;j++){
    					if(g[k][i] && g[k][j] && g[k][i]>g[k][j]){
    						f[i][j]=g[k][i]-g[k][j];
    					}
    				}
    			}
    		}
    		for(rg int k=1;k<=n;k++){
    			for(rg int i=1;i<=n;i++){
    				for(rg int j=1;j<=n;j++){
    					f[i][j]=std::min(f[i][j],f[i][k]+f[k][j]);
    				}
    			}
    		}
    		for(rg int i=1;i<=n;i++) ans=std::min(ans,f[i][i]);
    	}
    	if(ans==0x3f3f3f3f) ans=-1;
    	printf("%d
    ",ans);
    	return 0;
    }
    

    C. 密室逃脱

    分析

    (dp) 的定义还是很巧妙的

    对于一个区间 ([i,j]) 来说,如果这个区间里的人达到了一定的数量,那么这一群人一定想去区间里的那个地方就去哪一个地方

    我们定义这样的区间为一个段

    (f[i][j])(i) 当前所在的段内有 (j) 个人,可以放的最多人数

    (j<a[i]) 时,(i) 处的人无法打开门,如果在 (i+1) 处新加的人小于 (b[i]) 那就只能新开一个段了

    (f[i+1][0sim b[i]-1]=max(f[i+1][0sim b[i]-1],f[i][j]+0sim b[i]-1) jin[0,a[i]-1])

    如果新加的人恰好等于 (b[i]),那么 (i+1) 处的人就可以打开门

    (f[i+1][j+b[i]]=max(f[i+1][j+b[i]],f[i][j]+b[i]) jin[0,a[i]-1])

    (如果 i+1) 处新加的人大于 (b[i]) 时,我们完全可以看作 (b[i]) 个人开着门,剩下的人都去了 (i)

    这种转移在上面已经体现了,没有必要再更新一次

    (a[i] leq j < a[i]+b[i]) 时,

    (i) 个房间的人可以自己打开通道,但是为了保证第 (i) 个房间只能恰好 (j) 个人到达

    所以第 (i+1) 个房间一个人不能放,没有贡献,而且必须舍弃 (a[i]) 个人去开门

    (f[i+1][j−a[i]]=max(f[i+1][j-a[i]],f[i][j]))

    (j geq a[i]+b[i]) 时,

    和上面一样,但是不用留人去守门了

    (f[i+1][j]=max(f[i+1][j],f[i][j]))

    代码

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<iostream>
    #include<cstring>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=1e3+5,maxm=2e4+10;
    int n,m,a[maxn],b[maxn],mmax,f[2][maxm];
    int main(){
    	memset(f,~0x3f,sizeof(f));
    	n=read(),m=read();
    	for(rg int i=1;i<n;i++) a[i]=read(),b[i]=read(),mmax=std::max(mmax,std::max(a[i],b[i]));
    	mmax=std::max(mmax,m)*2+1;
    	rg int now=1;
    	for(rg int i=0;i<m;i++) f[1][i]=i;
    	for(rg int i=1;i<n;i++){
    		now^=1;
    		for(rg int j=0;j<=mmax;j++) f[now][j]=-0x3f3f3f3f;
    		rg int haha=-0x3f3f3f3f;
    		for(rg int j=0;j<a[i];j++) f[now][j+b[i]]=std::max(f[now][j+b[i]],f[now^1][j]+b[i]),haha=std::max(haha,f[now^1][j]);
    		for(rg int j=0;j<b[i];j++) f[now][j]=std::max(f[now^1][j],haha+j);
    		for(rg int j=a[i];j<a[i]+b[i];j++) f[now][j-a[i]]=std::max(f[now][j-a[i]],f[now^1][j]);
    		for(rg int j=a[i]+b[i];j<=mmax;j++) f[now][j]=std::max(f[now][j],f[now^1][j]);
    	}
    	rg int ans=-0x3f3f3f3f;
    	for(rg int i=0;i<=mmax;i++) ans=std::max(ans,f[now][i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    音乐欣赏之歌词-《我们的歌》
    Office2007能够直接发日记了
    唯美的十大经典句子
    音乐电影-特务J
    FIFA2008封面人物
    夜深了,你的手机关机吗?
    第一次的,也是最后的情书
    ACCESS定时远程备份
    SVN Server
    [Joomla] SQL加入Joomla用户
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14345963.html
Copyright © 2011-2022 走看看