排位赛#13题解
2024-10-21 16:02:03
发布于:上海
排位赛 #13 题解
写在前面
难了好多!第四题提交 2 次才 AC,呜呜呜,我菜得模拟都不会了。
个人评分:红红黄黄黄绿蓝紫。
T1
年龄分为今年之前的和今年之后的,直接处理比较麻烦。不妨将出生年移到今年,相应的下一场流星的时间也要修改,即 。然后把寿命的起点移到下一场流星,也就是 。最后的答案当然就是 。注意多测。
int T;scanf("%d",&T);
while(T--){
	int b,l,e;scanf("%d%d%d",&b,&l,&e);
	e=(e+b)%50;
	b=0;
	l-=e;
	
	if(l>=0)printf("%d\n",max(0,l/50+1));
	else puts("0");
}
T2
因为这是一道不卡时间的字符串处理,所以我们可以使用 Python 解题。注意特判越界。
t=int(input())
for i in range(t):
    s=input()
    if s in('o','co','rco','arco','marco'): # 防止越界
        print("MARCO")
    elif s[0:1]=='o':
        print("MARCO"+s[1:])
    elif s[0:2]=='co':
        print("MARCO"+s[2:])
    elif s[0:3]=='rco':
        print("MARCO"+s[3:])
    elif s[0:4]=='arco':
        print("MARCO"+s[4:])
    elif s[0:5]=='marco':
        print("MARCO"+s[5:])
    else:
        print(s)
T3
首先是无解的情况:,因为这样可以直接跳过去。
排除这种情况,我们考虑如何求出人数。发现“在每个长度 的连续区间内都有至少 个 TNT”是“至少 人可以通过”的充分必要条件,否则将不会有足够的 TNT 支撑玩家通过。因此,我们求出每一个长 的区间中 TNT 的数量,取最小值,这就是最多能成功通过 TNT 路径的玩家人数。
求数量的过程可以选择滑动窗口或前缀和,时空复杂度 。
char s[10005];
int main(){
	int T;scanf("%d",&T);
	while(T--){
		int n,k;
		scanf("%d%d%s",&n,&k,s+1);
		if(n<=k){
			puts("-1");
			continue;
		}
		k++;
		int cnt1=0,cnt2=0,ans=0x3f3f3f3f;
		for(int i=1;i<=k;i++){
			if(s[i]=='#')cnt1++;
			else cnt2++;
		}
		ans=cnt1;
		int l=1,r=k+1;
		while(r<=n){
			if(s[r]=='#')cnt1++;
			else cnt2++;
			if(s[l]=='#')cnt1--;
			else cnt2--;
			l++,r++;
			ans=min(ans,cnt1);
		}
		printf("%d\n",ans);
	}
	
	return 0;
}
T4
典中典之,大模拟。。。特别坑!
我们考虑把每一个牌用一个结构体存起来。存放点数时,我们按序用 map 映射点数,方便排序。然后,我们将牌按照第一点数,第二花色的顺序排序。最后按题意由优先级高到低判断——先判断三种顺子,如果不是顺子,就在桶排序后按照点数出现次数判断牌型。大坑:五张牌相同属于 High Card(高牌),而不是其它奇奇怪怪的牌型!被坑了一次提交23333。
代码可能比较抽象,凑合着看吧。
struct pai{
	int num;
	string col;
};  
bool cmp(pai x,pai y){
	if(x.num==y.num)return x.col<y.col;
	return x.num<y.num;
}
map<string,int>ctoin;
bool cmp2(int x,int y){
	return x>y;
}
int main(){
	ctoin["2"]=1;
	ctoin["3"]=2;
	ctoin["4"]=3;
	ctoin["5"]=4;
	ctoin["6"]=5;
	ctoin["7"]=6;
	ctoin["8"]=7;
	ctoin["9"]=8;
	ctoin["10"]=9;
	ctoin["J"]=10;
	ctoin["Q"]=11;
	ctoin["K"]=12;
	ctoin["A"]=13;
	
	int T;cin>>T;
	while(T--){
		string ch,s;
		pai a[10];
		for(int i=1;i<=5;i++){
			cin>>ch>>s;
			a[i].num=ctoin[ch];
			a[i].col=s;
		}
		sort(a+1,a+6,cmp);
		
		#define q(i)(a[i].num)
		#define w(i)(a[i].col)
		if(q(1)==9&&q(2)==10&&q(3)==11&&q(4)==12&&q(5)==13&&
		w(1)==w(2)&&w(2)==w(3)&&w(3)==w(4)&&w(4)==w(5)){
			puts("Royal Flush");
		}else if(q(2)==q(1)+1&&q(3)==q(2)+1&&q(4)==q(3)+1&&q(5)==q(4)+1&&
		w(1)==w(2)&&w(2)==w(3)&&w(3)==w(4)&&w(4)==w(5)){
			puts("Straight Flush");
		}else if(q(2)==q(1)+1&&q(3)==q(2)+1&&q(4)==q(3)+1&&q(5)==q(4)+1){
			puts("Straight");
		}else{
			int buc[15]={0};
			buc[q(1)]++;buc[q(2)]++;buc[q(3)]++;buc[q(4)]++;buc[q(5)]++;
			sort(buc+1,buc+15,cmp2);
			if(buc[1]==3&&buc[2]==2)puts("Full House");
			else if(buc[1]==4&&buc[2]==1)puts("Four of a Kind");
			else if(buc[1]==3)puts("Three of a Kind");
			else if(buc[1]==2&&buc[2]==2)puts("Two Pairs");
			else if(buc[1]==2)puts("One Pair");
			else puts("High Card");
		}
	}
		
	return 0;
} 
T5
如果 T4 是大模拟,这题就是小模拟。加一条边最多在两边各加一个小正方形,判断两边的正方形四边是不是都被加了即可。可以用简单的位运算技巧方便判断。注意分类讨论。
int vis[505][505];
int main(){
	int n,m,q;scanf("%d%d%d",&n,&m,&q);
	int x=0,y=0;
	while(q--){
		{
			int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
			if(a!=c){
				if(a>c)swap(a,c);
				// left
				vis[a][b-1]|=1;
				// right
				vis[a][b]|=2;
				if(vis[a][b-1]==15)x++;
				if(vis[a][b]==15)x++;
			}else{
				if(b>d)swap(b,d);
				// up
				vis[a-1][b]|=4;
				// down
				vis[a][b]|=8;
				if(vis[a-1][b]==15)x++;
				if(vis[a][b]==15)x++;
			}
		}
		{
			int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
			if(a!=c){
				if(a>c)swap(a,c);
				// left
				vis[a][b-1]|=1;
				// right
				vis[a][b]|=2;
				if(vis[a][b-1]==15)y++;
				if(vis[a][b]==15)y++;
			}else{
				if(b>d)swap(b,d);
				// up
				vis[a-1][b]|=4;
				// down
				vis[a][b]|=8;
				if(vis[a-1][b]==15)y++;
				if(vis[a][b]==15)y++;
			}
		}
	}
	printf("%d %d\n",x,y);
	
	return 0;
} 
T6
直接看比较麻烦,因为答案似乎被两个维度所限制。但是我们观察式子:
可以直接拆开:
要使其最小,就要使加号左右最小,而两边均只关于 和 。权值 很碍眼,由于 不大,不妨把每一个 拆分成 个 。设拆分后有 对坐标,则上式可改写为:
要使加号左边最小, 应该取什么呢?由绝对值性质得:
代入 得
求和得
等号成立,当且仅当 。所以要使原式最小, 的取值范围当然就是 。
当然右边关于  也是一样的。所以代码实现很简单,时间复杂度为 。log 可以用 nth_element 求中位数去掉,当然直接排也没事。
int w[50005];
double x[50005],y[50005];
int main(){
	int n;scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",w+i);
	double ansx=0,ansy=0;
	int nn=1;
	for(int i=1;i<=n;i++){
		scanf("%lf%lf",x+nn,y+nn);
		nn++;
		for(int j=2;j<=w[i];j++){
			x[nn]=x[nn-1];
			y[nn]=y[nn-1];
			nn++;
		}
	}
	nn--;
	sort(x+1,x+1+nn);
	sort(y+1,y+1+nn);
	ansx=x[(nn+1)/2],ansy=y[(nn+1)/2];
	double ans=0;
	for(int i=1;i<=nn;i++){
		ans+=fabs(ansx-x[i])+fabs(ansy-y[i]);
	}
	printf("%.5lf\n",ans);
	return 0;
} 
T7
如果你比较熟悉 FJ,你应该对一道典题不陌生——Corn Fields,是状压 DP 的板题。而这道题类似于本题,区别在于 变大了(),模数变了,同时不可以一只乌龟都不养。看起来 变大时间复杂度不行了,但是状压作法的复杂度略低于 , 变大的影响不大,所以还是状压板题。
设 是在第 行,当前行状态为 时的方案数,0 为不放,1 为放。我们知道不合法的方案有:行内有连续的 1,有 1 出现在养殖设备的位置。如果该行合法,我们枚举状态 。 同样需要满足上述条件,同时 与 不能有同位 1,因为行间也不能有连续的 1。如果一切都符合规则,则可以转移:。
别忘了边界条件:当 且 合法时,。
细节看注释。
int f[505][1<<11];
int a[505],st[1<<11];
#define Mod 1000000007
int main(){
	int n,m;scanf("%d%d",&n,&m);
	
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			int x;
			scanf("%d",&x);
			a[i]|=(!x)<<j; // 取反存数组
		}
	}
	
	for(int i=0;i<1<<m;i++){
		if(i&(i<<1))st[i]=1; // 每行二进制中不可以有两个连续的 1
	}
	
	for(int i=0;i<n;i++){
		for(int j=0;j<1<<m;j++){
			if(j&a[i]||st[j])continue; // 每行二进制中不可以有两个连续的 1,并且每一位都要是水池
			if(i!=0)for(int k=0;k<1<<m;k++){
				if(j&k||st[k])continue; // 每行每列二进制中不可以有两个连续的 1
				f[i][j]=(f[i][j]+f[i-1][k])%Mod; // 转移
			}else f[i][j]=1; // 初始化
		}
	}
	int ans=0;
	for(int i=0;i<1<<m;i++)ans=(ans+f[n-1][i])%Mod; // 统计答案
   if(ans==0)puts("0"); // 输出的一些小细节,自行理解
   else printf("%d\n",max(0,((ans-1)%Mod+Mod)%Mod));
	
	return 0;
}
T8
一道毒瘤的数据结构题。(数据结构题!)
数据范围是 ,暴力显然不行,可以考虑 的分块作法或者 的线段树作法。如果您很强,那么还有可能用一堆树状数组以优雅的小常数完成此题,不过我不会。这里用线段树作法。
首先是线段树的每个节点要存什么。显而易见的是加法懒标记和区间立方和的值。除此之外,由于高次和需要低次和推导而来,所以我们需要再维护区间平方和与区间和的值。
struct node{int l,r;ll sum1/*和*/,sum2/*平方和*/,sum3/*立方和*/,tag;}t[4*MAXN+5];
然后是线段树的核心——懒标记下传。
设当前区间的长度为 ,加法标记的值为 , 分别是修改前的 次方和, 分别是修改后的 次方和。
从最简单的说起:区间和当然增加了 ,也就是 。
然后是平方和:
最后是立方和:
(t[s(p)].sum3+=3*x*t[s(p)].sum2%Mod+3*x*x%Mod*t[s(p)].sum1%Mod+len*x%Mod*x%Mod*x%Mod)%=Mod;
(t[s(p)].sum2+=2*x*t[s(p)].sum1+len*x%Mod*x)%=Mod;
(t[s(p)].sum1+=x*len)%=Mod;
(t[s(p)].tag+=t[p].tag)%=Mod;
代码实现时,不要忘记上传节点信息的时候三种区间信息都要更新。为了防止负数出现,我们在输入的时候把所有负数取模成正数即可。
完整代码(稍有压行)。
#define MAXN 100000
#define Mod 1000000007
#define m(l,r) ((l+r)>>1)
#define ls(k) (k<<1)
#define rs(k) ((k<<1)|1)
struct node{int l,r;ll sum1,sum2,sum3,tag;}t[4*MAXN+5];
void build(int p,int l,int r){
	t[p].l=l,t[p].r=r;
	if(l==r)return;
	int mid=m(l,r);
	build(ls(p),l,mid);
	build(rs(p),mid+1,r);
}
void down(int p){
	if(t[p].tag){
		ll x=t[p].tag;
		int lenl=t[ls(p)].r-t[ls(p)].l+1;
		int lenr=t[rs(p)].r-t[rs(p)].l+1;
		
		#define len lenl
		#define s ls
		(t[s(p)].sum3+=3*x*t[s(p)].sum2%Mod+3*x*x%Mod*t[s(p)].sum1%Mod+len*x%Mod*x%Mod*x%Mod)%=Mod;
		(t[s(p)].sum2+=2*x*t[s(p)].sum1+len*x%Mod*x)%=Mod;
		(t[s(p)].sum1+=x*len)%=Mod;
		(t[s(p)].tag+=t[p].tag)%=Mod;
		#undef len
		#undef s
		
		
		#define len lenr
		#define s rs
		(t[s(p)].sum3+=3*x*t[s(p)].sum2%Mod+3*x*x%Mod*t[s(p)].sum1%Mod+len*x%Mod*x%Mod*x%Mod)%=Mod;
		(t[s(p)].sum2+=2*x*t[s(p)].sum1+len*x%Mod*x)%=Mod;
		(t[s(p)].sum1+=x*len)%=Mod;
		(t[s(p)].tag+=t[p].tag)%=Mod;
		#undef len
		#undef s
		
		t[p].tag=0;
	}
}
void change(int p,int x,int y,int z){
	if(x<=t[p].l && y>=t[p].r){
		int len=(t[p].r-t[p].l+1);
		ll x=z;
		(t[p].sum3+=3*x*t[p].sum2%Mod+3*x*x%Mod*t[p].sum1%Mod+len*x%Mod*x%Mod*x%Mod)%=Mod;
		(t[p].sum2+=2*x*t[p].sum1+len*x%Mod*x)%=Mod;
        (t[p].sum1+=x*len)%=Mod;
		(t[p].tag+=z)%=Mod;
        return;
    }
    down(p);
    int mid=m(t[p].l,t[p].r);
    if(x<=mid) change(ls(p),x,y,z);if(y>mid) change(rs(p),x,y,z);
    (t[p].sum1=t[ls(p)].sum1+t[rs(p)].sum1)%=Mod; (t[p].sum2=t[ls(p)].sum2+t[rs(p)].sum2)%=Mod; (t[p].sum3=t[ls(p)].sum3+t[rs(p)].sum3)%=Mod; 
}
int ask(int p,int x,int y){
    if(x<=t[p].l && y>=t[p].r) return t[p].sum3;
    down(p);
    int mid=m(t[p].l,t[p].r);
    int ans=0;
    if(x<=mid) ans+=ask(ls(p),x,y);
    if(y>mid) ans+=ask(rs(p),x,y);
    ans%=Mod;
    return ans;
}
int main(){
	int n,m;scanf("%d%d",&n,&m);
	build(1,1,n);
	for(int i=1;i<=n;i++){int x;scanf("%d",&x);change(1,i,i,(x%Mod+Mod)%Mod);}
	while(m--){
		int op,l,r,k;scanf("%d%d%d",&op,&l,&r);
		if(op==1){scanf("%d",&k);k=(k%Mod+Mod)%Mod;change(1,l,r,k);}
      else printf("%d\n",ask(1,l,r));
	}
		
	return 0;
} 
尾
祝大家 CSP-J2/S2 rp++!
unsigned long long rp=0;
rp--;
全部评论 8
- 如果觉得好可否点个赞~ - 2024-10-08 来自 上海 3- 贺榜一! - 2024-10-08 来自 福建 1
- 谢谢! - 2024-10-08 来自 上海 0
- 秒回 - 2024-10-08 来自 福建 0
 
- 太牛了,点赞! - 2024-10-08 来自 加拿大 2
- 顶 - 2024-10-10 来自 广东 0
- 顶 - 2024-10-09 来自 广东 0
- 顶 - 2024-10-09 来自 上海 0
- 第一!!! - 2024-10-09 来自 天津 0
- 看完后直接爽了 - 2024-10-08 来自 广东 0
- 顶 - 2024-10-08 来自 广东 0



















有帮助,赞一个