2020牛客寒假算法基础集训营3——F.牛牛的Link Power I【计算】【分治】

题目传送门


题目描述

牛牛有一颗大小为n的神奇 L i n k C u t Link-Cut 数组,数组上的每一个节点都有两种状态,一种为 l i n k link 状态,另一种为 c u t cut 状态。数组上任意一对处于 l i n k link 状态的无序点对(即 ( u , v ) (u,v) ( v , u ) (v,u) 被认为是同一对)会产生 d i s ( u , v ) dis(u,v) l i n k link 能量, d i s ( u , v ) dis(u,v) 为数组上u到v的距离。

我们定义整个数组的 L i n k Link 能量为所有处于 l i n k link 状态的节点产生的 l i n k link 能量之和。

一开始数组上每个节点的状态将由一个长度大小为 n n 01 01串 给出, 1 L i n k '1'表示Link状态 0 C u t '0'表示Cut状态

牛牛想要知道整个数组的 L i n k Link 能量,为了避免这个数字过于庞大,你只用输出答案对 1 0 9 + 7 10^9+7 取余后的结果即可。


输入描述:

第一行输入一个正整数 n ( 1 n 1 0 5 ) n(1 \leq n \leq 10^5)

接下里一行输入一个长度大小为n的01串表示数组的初始状态,'1’表示Link状态,'0’表示Cut状态。


输出描述:

仅一行,表示整个数组的Link能量对 1 0 9 + 7 10^9+7 取余后的


输入

3
101
5
00110
6
000010


输出

2
1
0


题解1 (直接计算)

  • c n t n u m c n t 1 , n u m 记录cnt与num,cnt表示当前经过了多少个1,num表示前面所有的点到当前位置的贡献。
  • 这个也是最直观最好想到,最有效的算法。
  • O ( n ) , O ( 1 ) 因为他时间复杂度为O\left(n\right),空间复杂度甚至可以O\left( 1\right)

题解2 (分治)

  • 分治算法计算贡献,对于每一个区间 [ l , r ] \left[ l,r \right] ,它的中点为 m i d mid ,该区间的贡献可以分解为以下三部分

    1. 左侧区间产生的贡献。
    2. 右侧区间产生的贡献。
    3. 过中点 m i d mid 产生的新贡献。
  • 那么对于1,2,我们可以使用递归求解,由于区间越分越小,所以最终复杂度总和为 O ( n l o g 2 n ) O\left(nlog_2n\right)

  • 考虑3,只需要暴力for左侧区间,统计左侧区间"1"的数目,以及它们到中点的和。
    时空复杂度为 O ( n l o g 2 n ) O\left(nlog_2n\right)

  • 这个方法虽然不够优秀,但是它可以扩展到线段树上面维护动态问题(也就是解决第二问),线段树其实就是保留了每次分治的结果。

  • 如果学分治的话建议写一写。


AC-Code ( ) (直接计算)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn = 1e5 + 7;
const int mod = 1e9 + 7;
 
int main() {
    ios;
    int n; string s;
    cin >> n >> s;
    ll ans = 0;
    ll num = 0;
    int cnt = 0;
    for (int i = 0; i < s.size(); ++i) {
        num = (num + cnt) % mod;    // 如果前面有cnt个1,那么每个1的位置往当前位置的距离都需要+1
        if (s[i] == '1') {
            if (cnt)
                ans = (ans + num) % mod;
            ++cnt;
        }
    }
    cout << ans << endl;
}

AC-Code ( ) (分治)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int mod = 1e9 + 7;
char s[MAXN];
int n;
long long ans;
void div_algorithm(int l, int r) {
	if (l == r)     return;
	int mid = (l + r) >> 1;
	long long leftsum = 0;
	long long leftcnt = 0;
	for (int i = mid; i >= l; --i) {	
		if (s[i] == '1') {
			leftsum = (leftsum + mid - i) % mod;
			++leftcnt;
		}
	}
	for (int i = mid + 1; i <= r; ++i) {
		if (s[i] == '1') {
			ans = (ans + leftsum + (i - mid) * leftcnt % mod) % mod;
		}
	}
	div_algorithm(l, mid);		// 计算左侧贡献
	div_algorithm(mid + 1, r);	// 计算右侧贡献
}
int main()
{
	scanf("%d", &n);
	scanf("%s", s + 1);
	div_algorithm(1, n);
	printf("%lld\n", ans);
	return 0;
}
发布了162 篇原创文章 · 获赞 99 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Q_1849805767/article/details/104229409