2021-7-12 个人赛 补题

B:Above the Median

题意:给你一个大小100000的序列,求出有多少区间他们排序之后的中位数大于等于X。

最开始的想法就是大于等于X的数赋值为1,小于X的数赋值为0,那么满足条件的区间就是 区间和大于等于区间长度的一半( sum > = (len + 1) / 2 )
考虑优化 n²的复杂度,参考倍增的思想,如果一个区间中1的数量为 x,此时的区间长度为 len,那么此时的指针可以直接跳到(2*x+1)的位置,此时不管跳过的区间中 0 / 1 的情况是怎么样的,都满足条件,那么直接加到贡献里就好了。
已知这种跳指针的方法在1的个数很多的时候很有效,为了避免 TLE,在 0 多的时候,采用同样的跳法来减就好了。(虽然我也不会算这个方法的复杂度,但是过了,还挺快的);

ll t,n,m;
int s[100050];
int main(){
	scanf("%lld%lld",&n,&m);
	int cnt=0;
	for(int i=1;i<=n;i++){
		ll a;scanf("%lld",&a);
		if(a>=m)s[i]=1;
		else s[i]=0;
		if(s[i]==1){
			cnt++;
		}
		s[i]=s[i]+s[i-1];
		
	}
	if(cnt>=n/2){
		ll ans=0;
		for(int i=1;i<=n;i++){
			int now=s[i]-s[i-1];
			int p=i;
			while(p<=n){
				if(s[p]-s[i-1]>=(p-i+2)/2){
					ans++;
				}
				int q=max((s[p]-s[i-1])*2+i,p+1);
				if(q<=n)ans+=q-p-1;
				else ans+=n-p;
				p=q;
			}
		}
		printf("%lld\n",ans);
	}else{
		ll ans=n*(n+1)/2;
		for(int i=1;i<=n;i++){
			int now=s[i]-s[i-1];
			int p=i;
			while(p<=n){
				if(s[p]-s[i-1]<(p-i+2)/2){
					ans--;
				}
				int q=max((p-i+1-s[p]+s[i-1])*2+i-1,p+1);
				if(q<=n)ans-=q-p-1;
				else ans-=n-p;
				p=q;
			}
		}
		printf("%lld\n",ans);
	}
	
	return 0;
}

好了,上正解:
取大于等于X的数为1,小于X的数为 -1,这样满足条件的区间就是sum>=0;
这样我们用前缀和来维护,即 s [r] - s[l-1] >= 0,等价于s [r] >= s[l-1];
用树状数组维护 正序对即可。

ll t,n,m;
ll s[100050];
int tree[400050];
int lowbit(int x){
	return x&(-x);
}
void add(int i){
	while(i<=n+1e5+5){
		tree[i]+=1;
		i+=lowbit(i);
	}
}
ll query(int x,int y){
	ll ans1=0,ans2=0;
	while(y>0){
		ans2+=tree[y];
		y-=lowbit(y);
	}
	while(x>0){
		ans1+=tree[x];
		x-=lowbit(x);
	}
	return ans2-ans1;
}
int main(){
	scanf("%lld%lld",&n,&m);
	s[0]=0;
	for(int i=1;i<=n;i++){
		ll a;scanf("%lld",&a);
		if(a>=m)s[i]=1;
		else s[i]=-1;
		s[i]+=s[i-1];
	}
	ll ans=0;
	for(int i=n;i>=1;i--){
		add(s[i]+1e5);
		ans+=query(s[i-1]+1e5-1,1e5+n+5);
	}
	printf("%lld\n",ans);
	return 0;
}

扩展:
因为我们对于每个数的权值都是自己赋值的,
那么,如果题目要求我们求出区间中三分之一点是 大于等于X的,我们只需要 令大于等于X的数 为1,小于X的数 为-2;
再求有多少区间的和 sum>=0 即可。

K:Tile Exchanging

给你n个数(n<=10),对他们进行修改,使他们的平方和等于M(M<=10000);
每个数只能修改一次,修改的代价为(abs(修改后的数-修改前的数) ) ² ;
求最小花费。

第一想法贪心。(唉,dp不会,遇到题目只会想贪心;下次发现贪不出来就想想dp叭)
题解:
dp [i] [j] 表示前 i 个数,平方和为 j 的最小花费
枚举下一个数修改后的值进行转移

时间复杂度 O ( N ∗ M ∗ M A X ( A i ) )

ll n,m;
ll s[20];
ll dp[11][10060]; 
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)scanf("%lld",&s[i]);
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++)dp[i][j]=INF;
	}
	dp[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			for(int k=1;k<=sqrt(m);k++){
				if(j<k*k)break;
				dp[i][j]=min(dp[i][j],dp[i-1][j-k*k]+abs(s[i]-k)*abs(s[i]-k));
			}
		}
	}
	if(dp[n][m]==INF)printf("-1\n");
	else printf("%lld\n",dp[n][m]);
	return 0;
}

L:Binary Sudoku

题意:
给你 99 的 0/1 方格,执行最少的翻转操作使得 每一行 每一列 每一个33 的方格都含有偶数个1
求最少操作数;
解法:
dp[i] [j] [k] 表示现在处理完第 i 行,此时9列的状压值表示为 j ,目前行的那3个 3 × 3 的格子内的状压值表示为 k ;

抄代码:
预处理出 初始每行的状压值;
以及当前这一行的状压值时,第一个33的方格中1 的个数,第二个…第三个…以及这一行中1 的个数;
转移:
在3
3的方格中的第一行,列的状压值就是原列的状压值异或这行的状压值,每一个方格的状压值,就是当前行的状压值对个个方格的贡献。
第二行,列的状压值就是原列的状压值异或这行的状压值,每一个方格的状压值就是上一行的方格的状压值和当前行的贡献的异或
第三行的状压值,只转移使得最后三个方格的奇偶性为偶数的情况。

花费就是改变的状压值中1的数量

int n=9;
char s[10];
int dp[10][513][9];
int a[10];
int p[513][4]; 
int main(){
	for(int i=1;i<=n;i++){
		scanf("%s",s);
		for(int j=0;j<n;j++){
			a[i]=a[i]|((s[j]-'0')<<j);
		}
	}
	
	for(int i=0;i<(1<<n);i++){
		for(int j=0;j<3;j++)p[i][1]+=((i>>j)&1);	//第一个方格中的1的数量 
		for(int j=3;j<6;j++)p[i][2]+=((i>>j)&1);	//第二个方格 
		for(int j=6;j<9;j++)p[i][3]+=((i>>j)&1);	//第三个方格 
		p[i][0]=p[i][1]+p[i][2]+p[i][3];	//当前行的1的个数 
	}
	
	for(int i=0;i<=n;i++){
		for(int j=0;j<(1<<n);j++){
			for(int k=0;k<(1<<3);k++)dp[i][j][k]=INF;
		}
	}
	dp[0][0][0]=0;
	for(int i=1;i<=9;i++){
		for(int j=0;j<(1<<n);j++){
			for(int k=0;k<(1<<3);k++){
				for(int w=0;w<(1<<n);w++){
					if(!(p[w][0]&1)){
						int jj=j^w;	//*当前行的列的状压等于之前列的状压 *异或* (符合奇偶运算)
									//这一行的值。* 
						int kk=0;
						if(i%3==1){
							kk=(p[w][1]&1)|((p[w][2]&1)<<1)|((p[w][3]&1)<<2);
							dp[i][jj][kk]=min(dp[i][jj][kk],dp[i-1][j][k]+p[w^a[i]][0]);
						}else if(i%3==2){
							kk=k^((p[w][1]&1)|((p[w][2]&1)<<1)|((p[w][3]&1)<<2));
							dp[i][jj][kk]=min(dp[i][jj][kk],dp[i-1][j][k]+p[w^a[i]][0]);
						}else{
							if(((k>>0)&1)^(p[w][1]&1))continue;
							if(((k>>1)&1)^(p[w][2]&1))continue;
							if(((k>>2)&1)^(p[w][3]&1))continue;
							dp[i][jj][0]=min(dp[i][jj][0],dp[i-1][j][k]+p[w^a[i]][0]);
						}
					}
				}
			}
		}
	}
	printf("%d\n",dp[9][0][0]);
	return 0;
}

补充:
0-1 bfs
用deque实现,花费为0放队首,花费为1放队尾
加标记只能在出队的时候加,在ans[i] [j]不等于inf时,continue;


int n,m,fx,fy,ex,ey,vis[N][N],way[4][2] = {{1,0},{-1,0},{0,1},{0,-1}},ans[N][N];
char str[N][N];
struct node
{
    int x,y,step;
};
deque<node> q;
signed main()
{
    std::ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //    freopen("input.txt","r",stdin);
    //    freopen("output.txt","w",stdout);
    cin >> n >> m >> fx >> fy >> ex >> ey;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            cin >> str[i][j],ans[i][j] = INF;
    q.push_back({fx,fy,0});
    while(!q.empty())
    {
        node t = q.front();q.pop_front();
        int x = t.x,y = t.y,step = t.step;
        if(ans[x][y]!=INF) continue;//只能这样记忆化,就因为他是双端队列的bfs,而不是在下面找到点就直接记忆化了
        ans[x][y] =step;
//        cout << x << " " << y << " " << step << " " << ans[x][y] << endl;
        for(int i = 0;i < 4;i++)//先入队代价为0
        {
            int xx = x+way[i][0],yy = y+way[i][1];
            if(xx > 0 && xx <= n && yy > 0 && yy <= m && !vis[xx][yy] && str[xx][yy] == '.')
                q.push_front({xx,yy,step});
        }
        for(int i = -2;i <= 2;i++)//再入队代价为1
        {
            for(int j = -2;j <= 2;j++)
            {
                int xx = x+i,yy = y+j,flag = 0;
                if(xx > 0 && xx <= n && yy > 0 && yy <= m && !vis[xx][yy] && str[xx][yy] == '.')
                    q.push_back({xx,yy,step+1});
            }
        }
    }
    if(ans[ex][ey] == INF) cout << -1 << endl;
    else cout << ans[ex][ey] << endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值