模拟赛2021.11.4总结

2021.11.4

总结

这一次模拟赛还是考的不好。考试的时候,一有点想法就逮到半截就开跑了,然后没想周全,然后编码之后发现错了浪费了很多时间。
T1:做这个题的时候一开始是想错了策略,想用分块去维护二维数点,但是发现没有办法维护竖直方向的前缀和,所以寄了,然后就急急忙忙的写了一发暴力,然后才发现只有100种长度,可以维护不同长度的前缀和,然后继续写了一个分块,但是由于以为这个题目的时限比较死,所以块长取根号萎了。本身期望AC的,但是T了,下来之后改了块长就过了。
T2:推得时候,没有想清楚,结果代码写了一半,发现萎了,但已经浪费了很多时间,最后只有匆忙写暴力,(考试的时候想到了暴搜,但是觉得直接暴搜会超时)
T3:看了一会发现根本不会做,然后觉得暴力的分也会T,所以就没敢写,但是试后跟巨佬讨论,发现这个题没有刻意的卡暴力,所以就是敢写就敢得分, R P − − RP-- RP
T4:我看到这个题的时候想到了用笛卡尔树维护所以过于兴奋,加上时间不多了,然后一着急连笛卡尔树搞忘了该怎么写了,还是之前掌握的不牢,结果,就是保龄了。(已经狠狠回顾过笛卡尔树了)

题解

算面积

solution:

我们可以将不同长度分组,每一串对应的前缀和,然后以 n \sqrt n n 的长度压缩这些前缀和
在询问的时候,对于整块可以 O ( 100 ) O(100) O(100)完成,散块由于块长可以设置成小于100,所以也可以暴力 O ( 100 ) O(100) O(100)完成

#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
int n,m,Q,nn;
ll sum[N][105],c[1005][105][105];
char S[105];
ll work(int x,int y,int p,int q){
	ll res=0;
	for(int i=x;i<=p;i++){
		int a=(y-1)%sum[i][0];a=(!a)?sum[i][0]+1:a;
		int b=q%sum[i][0];b=(!b)?sum[i][0]+1:b;
		ll tmp=-sum[i][sum[i][0]]*((y-1)/sum[i][0])-sum[i][a]+
		sum[i][sum[i][0]]*(q/sum[i][0])+sum[i][b];
		res+=tmp;
	}return res;
}
ll solve(int x,int y,int p,int q){
	ll res=0;
	int fx=(x-1)/nn+1,fp=(p-1)/nn+1;
	if(fx==fp){
		res=work(x,y,p,q);
	}else {
		res=work(x,y,fx*nn,q)+work((fp-1ll)*nn+1,y,p,q);
		for(int len=1;len<=100;len++){
			int a=(y-1)%len;a=(!a)?len+1:a;
			int b=q%len;b=(!b)?len+1:b;
			ll tmp1=-c[fx][len][len]*((y-1)/len)-c[fx][len][a]+
			c[fx][len][len]*(q/len)+c[fx][len][b];
			ll tmp2=-c[fp-1][len][len]*((y-1)/len)-c[fp-1][len][a]+
			c[fp-1][len][len]*(q/len)+c[fp-1][len][b];
			res+=tmp2-tmp1;
		}
	}return res;
}
int main(){
	scanf("%d%d",&n,&m);
	nn=sqrt(n);
	nn=min(nn,100);int x=0;
	for(int i=1;i<=n;i++){
		if(i%nn==1)x++;
		scanf("%s",S+1);int len=strlen(S+1);
		for(int j=1;j<=len;j++)sum[i][j]=sum[i][j-1]+(S[j]-'0'),
		c[x][len][j]+=sum[i][j];
		sum[i][0]=len;
	}
	for(int i=1;i<=n/nn;i++)
	for(int len=1;len<=100;len++)
	for(int j=1;j<=len;j++)c[i][len][j]+=c[i-1][len][j];
	scanf("%d",&Q);
	for(int i=1;i<=Q;i++){
		int x,y,p,q;scanf("%d%d%d%d",&x,&y,&p,&q);
	 	printf("%lld\n",solve(x,y,p,q));
	}
	return 0;
}

猜数

solution:

我们可以发现对于题目的定义,那么确定位数之后对于每个首尾对应的位置的和应该只有唯一解,因为,我们一定是从两头往中间确定,那么是环环相扣的,所以感性理解一定是唯一解。
那么们可以采取暴搜,分两种情况,一种是跟给出的数位数相同,一种是位数少1。复杂度十分的正确,就只有 O ( 9 ) O(9) O(9).
那么我们可以看到,会存在几种情况(设定 x , y x,y x,y首尾对应的位置)

{ 没 有 变 化 , ( a [ x ] = a [ y ] ) a [ x − 1 ] + = 10 , ( a [ x ] − 1 = a [ y ] ) a [ y + 1 ] − − , ( a [ x ] = a [ y ] + 10 ) a [ y + 1 ] − − , a [ x − 1 ] + = 10 , ( a [ x ] − 1 = a [ y ] + 10 \left\{ \begin{matrix} 没有变化,(a[x]=a[y])\\ a[x-1]+=10,(a[x]-1=a[y]) \\ a[y+1]--, (a[x]=a[y]+10)\\ a[y+1]--,a[x-1]+=10,(a[x]-1=a[y]+10 \end{matrix} \right. ,(a[x]=a[y])a[x1]+=10,(a[x]1=a[y])a[y+1],(a[x]=a[y]+10)a[y+1],a[x1]+=10,(a[x]1=a[y]+10

然后对边界处理一下就好了,细节在代码中。

#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
ll n,ans,cnt,num[20],jin[20],w[20],a[20];
char S[20];
ll dfs(int l,int r){
	if(l==r)return (!(a[l]&1));
	if(l==r+1){
		if(a[l]==a[r])return 1;
		if(a[l]-1==a[r]+10){
			a[l]--;a[r]+=10;return 1;
		}
	}
	if(a[l]==a[r])return dfs(l-1,r+1);
	if(a[l]-1==a[r]){a[l]--;a[l-1]+=10;return dfs(l-1,r+1);}
	if(a[l]==a[r]+10){a[r+1]-=1;a[r]+=10;return dfs(l-1,r+1);}
	if(a[l]-1==a[r]+10){a[l]--;a[l-1]+=10;a[r+1]-=1;return dfs(l-1,r+1);}
	return 0;
}
signed main()
{
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	int T;scanf("%d",&T);
	while(T--){
		scanf("%s",S+1);cnt=strlen(S+1);ans=0;
		for(int i=1;i<=cnt;i++)	num[cnt-i+1]=S[i]-'0';
		memcpy(a,num,sizeof a);
		if(dfs(cnt,1)){
			ll res=1;
			for(int i=cnt;i>((cnt+1)>>1);i--){
				if(i==cnt-i+1)break;
				if(i==cnt)res*=min(a[i],19ll-a[i]);
				else res*=min(a[i]+1,19ll-a[i]);
			}
			ans+=res;
		}
		memcpy(a,num,sizeof a);
		a[cnt-1]+=10;
		if(a[cnt]==1&&dfs(cnt-1,1)){
			ll res=1;
			for(int i=cnt-1;i>((cnt-1)>>1);i--){
				if(i==cnt-i)break;
				if(i==cnt-1)res*=min(a[i],19ll-a[i]);
				else res*=min(a[i]+1,19ll-a[i]);
			}
			ans+=res;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

排序

solution:

Guxue神犇想出来的绝妙办法
我们可以显然的发现,一个数肯定定会排名比自己大一个的数稳定之后再进行操作,那么我们用 r d rd rd记录轮数,表示每个数会被操作几次,用 r t rt rt来记录上一个排名的数没有跟上上排名的数轮数相同的最右边的数字,因为我们要用 r t rt rt来判断,当前排名的数是否跟是跟上一个排名的人同一轮删掉,如果当前数在 r t rt rt左边,说明这个数会在当前的轮数基础上在操作一次。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{int x,id;}a[1000010];
bool cmp(node x,node y){return (x.x==y.x)?x.id<y.id:x.x<y.x;}
int n,up,rd;
ll ans;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i].x),a[i].id=i;
	sort(a+1,a+1+n,cmp);
	for(int i=1,lst=-1;i<=n;i++){
		if(a[i].x!=lst) ++up,lst=a[i].x;
		a[i].x=up;
	}for(int i=up,rd=0,j=n,rt=0;i;i--){
		int pos=a[j].id,flag=1;
		while(a[j].x==i){
			if(flag&&a[j].id<rt)++rd,pos=a[j].id,flag=0;
			ans+=rd;--j;
		}rt=pos;
	}printf("%lld\n",ans);return 0;
}

水池

solution:
在这里插入图片描述

对于这样的水池,我们可以去建立一个以平台高度为关键字的小根笛卡尔树,保证了儿子对父亲的贡献不会被更高的平台所阻挡。
然后建出树以后,排出的水,就是书上若干条链的权值总和,因为贪心,我们一定是从叶子结点选链,然后我们可以长链剖分,对于所有的长链进行排序,选择其中最长的若干条,作为出水口。

#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e5+10;
int n,m,x[N],y[N];
struct node{
	ll x[2],y;
}p[N];int cnt;
int st[N],ed,son[N][2];
ll to[N][2],ft[N][2],line[N],ans;
priority_queue<ll,vector<ll>,less<ll> >Q;
void dfs(int u){
	
	ft[u][0]=p[u].x[0];
	ft[u][1]=p[u].x[1];
	int ls=son[u][0],rs=son[u][1];
	if(ls){
		dfs(ls);
		ft[u][0]=ft[ls][0];
		to[u][0]=(p[ls].y-p[u].y)*(ft[ls][1]-ft[ls][0]);
		line[u]=line[ls]+to[u][0];
	}if(rs){
		dfs(rs);ft[u][1]=ft[rs][1];
		to[u][1]=(p[rs].y-p[u].y)*(ft[rs][1]-ft[rs][0]);
		if(line[u]<line[rs]+to[u][1]){
			if(line[u])Q.push(line[u]);
			line[u]=line[rs]+to[u][1];
		}else Q.push(line[rs]+to[u][1]);
	}
//	printf("%d - %lld\n%lld %lld %lld\n",
//	u,line[u],p[u].x[0],p[u].x[1],p[u].y);
}
void build(){
	ed=0;
	for(int i=1;i<=cnt;i++){
		while(ed&&p[st[ed]].y>p[i].y)son[i][0]=st[ed--];
		if(ed)son[st[ed]][1]=i;
		st[++ed]=i;
	}dfs(st[1]);
}
signed main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",x+i,y+i);
		if(i!=1&&y[i]==y[i-1])p[++cnt]=(node){x[i-1],x[i],y[i]};
	}build();scanf("%d",&m);
	m--;ans=p[st[1]].y*1ll*(x[n]-x[1])+line[st[1]];
	while(Q.size()&&m--)ans+=Q.top(),Q.pop();
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值