• 洛谷 P8313 [COCI2021-2022#4] Izbori


    PS:如果读过题了可以跳过题目描述直接到题解部分
    提交链接:洛谷 P8313 [COCI2021-2022#4] Izbori

    题目

    题目描述

    Malnar 先生正在竞选县长,这个县一共有 n n n 栋房屋,每栋房屋里都住着一位居民。Malnar 先生知道,选举的赢家不一定是最好的候选人,而是在选举前举办的宴会最好的候选人。因此,在选举前几天,他将邀请第 l l l r ( l ≤ r ) r(l\le r) r(lr) 栋房屋内居住的居民,为他们准备一顿丰盛的晚餐。

    Malnar 先生知道所有居民最喜欢吃的菜。在宴会上,他会准备大多数人喜欢的一道菜。如果一个人吃到了自己最喜欢吃的菜,将会投一票给 Malnar 先生。但是如果没有吃到自己最喜欢吃的菜,他们将会把票投给 Vlado 先生。如果没有来参加晚宴的居民,他们将会忘记选举,不做出任何投票。如果一个候选人获得了投了票的人中一半以上的人的支持,他将会赢得竞选。

    Malnar 先生想知道,有多少组的 ( l , r ) (l,r) (l,r) 可以使他赢得竞选。

    输入格式

    第一行包含一个整数 n n n,表示房屋的数量。

    第二行包含 n n n 个整数 a i a_i ai,表示第 i i i 栋房屋内居民最喜欢的菜。

    输出格式

    仅一行,输出可以使 Malnar 先生赢得竞选的 ( l , r ) (l,r) (l,r) 的组数。

    样例 #1

    样例输入 #1

    2
    1 1
    
    • 1
    • 2

    样例输出 #1

    3
    
    • 1

    样例 #2

    样例输入 #2

    3
    2 1 2
    
    • 1
    • 2

    样例输出 #2

    4
    
    • 1

    样例 #3

    样例输入 #3

    5
    2 2 1 2 3
    
    • 1
    • 2

    样例输出 #3

    10
    
    • 1

    提示

    【样例 2 解释】

    可以使 Malnar 先生赢得竞选的 ( l , r ) (l,r) (l,r) 为: ( 1 , 1 ) , ( 2 , 2 ) , ( 3 , 3 ) , ( 1 , 3 ) (1, 1),(2, 2),(3, 3),(1, 3) (1,1),(2,2),(3,3),(1,3)

    【数据规模与约定】

    本题采用子任务捆绑测试。

    • Subtask 1(10 pts): 1 ≤ n ≤ 300 1 ≤ n ≤ 300 1n300
    • Subtask 2(15 pts): 1 ≤ n ≤ 2 × 1 0 3 1 ≤ n ≤ 2\times10^3 1n2×103
    • Subtask 3(15 pts): ∀ i ∈ { 1 , 2 , 3 , … , n } , 1 ≤ a i ≤ 2 \forall i\in\{1,2,3,\dots,n\},1 ≤ a_i ≤ 2 i{1,2,3,,n},1ai2
    • Subtask 4(70 pts):没有额外限制。

    对于 100 % 100\% 100% 的数据, 1 ≤ l ≤ r ≤ n ≤ 2 × 1 0 5 , 1 ≤ a i ≤ 1 0 9 1 \le l\le r ≤ n ≤ 2\times10^5,1 ≤ a_i ≤ 10^9 1lrn2×105,1ai109

    【提示与说明】

    本题分值按 COCI 原题设置,满分 110 110 110

    题目译自 COCI2021-2022 CONTEST #4 T3 Izbori。

    题解

    25pts

    O ( n 3 ) O(n^3) O(n3)的暴力
    直接暴力累加计算就好了。
    但其实累加的时候会有一个想法: s u m j − ( j > > 1 ) > s u m i − 1 − ( ( i − 1 ) > > 1 ) sum_{j}-(j>>1)>sum_{i-1}-((i-1)>>1) sumj(j>>1)>sumi1((i1)>>1) 即 2 × s u m j − j > 2 × s u m i − 1 − i − 1 即2\times sum_{j}-j>2\times sum_{i-1}-i-1 2×sumjj>2×sumi1i1
    这就推出了我们的正解的想法。

    110pts

    我们再用一个数组 c c c来保存 2 × s u m i − i 2\times sum_i-i 2×sumii的值。
    c i = 2 × s u m i − i c_i=2\times sum_i-i ci=2×sumii
    因为事实上我们只需要 c c c的相对大小,并且每一段的 c c c都是一段公差为 1 1 1的等差数列,所以可以用类似于桶的想法,用一个数组 d d d来保存不同值的 c c c的个数。
    d i = Σ j = 1 i c j d_i=\Sigma_{j=1}^ic_j di=Σj=1icj
    于是我们就又会发现,对于一段 c c c,产生的贡献又是对应这一段的 Σ d i \Sigma d_i Σdi,所以可以再用数组 e e e保存数组 d d d的前缀和。
    e i = Σ j = 1 i d j e_i=\Sigma_ {j=1}^id_j ei=Σj=1idj
    同时我们需要进行区间修改并求前缀和,所以我们又可以将其改为单点修改并求前缀和,此时,我们还需要一个数组 f f f

    总结一下上面的数组:(分不同种类的数)
    s u m j = Σ i = 1 j a i sum_j=\Sigma_{i=1}^ja_i sumj=Σi=1jai
    c j = 2 × s u m j − j c_j=2\times sum_j-j cj=2×sumjj
    d k = Σ j = 1 n ( c j = = k ) (需要被区间修改) d_k=\Sigma_{j=1}^n(c_j==k)(需要被区间修改) dk=Σj=1n(cj==k)(需要被区间修改)
    e i = Σ k = 1 i d k e_i=\Sigma_{k=1}^id_k ei=Σk=1idk
    f l = Σ l = 1 i e l f_l=\Sigma_{l=1}^ie_l fl=Σl=1iel
    所以维护三个树状数组就行。

    代码实现

    25pts

    //洛谷 P8313 [COCI2021-2022#4] Izbori
    #pragma GCC optimize(3)
    #include
    #include
    #include
    #include
    using namespace std;
    int n;
    int a[200010];
    int b[200010];
    int sum[200010];
    int cnt;
    int x;
    long long ans;
    
    int find(int x){//二分查找
    	int l=0,r=cnt,mid;
    	while(l<r){
    		mid=(l+r+1)>>1;
    		if(b[mid]<=x){
    			l=mid;
    		}
    		else{
    			r=mid-1;
    		}
    	}
    	return l;
    }
    
    void in(int &x){
    	int nt;
    	x=0;
    	while(!isdigit(nt=getchar()));
    	x=nt^'0';
    	while(isdigit(nt=getchar())){
    		x=(x<<3)+(x<<1)+(nt^'0');
    	}
    }
    
    int main(){
    	register int i,j;
    	in(n);
    	for(i=1;i<=n;++i){
    		in(a[i]);
    		b[i]=a[i];
    	}
    	sort(b+1,b+n+1);
    	cnt=unique(b+1,b+n+1)-(b+1);//离散化
    	for(i=1;i<=n;++i){
    		x=find(a[i]);
    		++sum[x];
    		++ans;
    		for(j=i+1;j<=n;++j){
    			x=++sum[find(a[j])]>sum[x]?find(a[j]):x;
    			if(sum[x]>=((j-i+1)>>1)+1){
    				++ans;
    			}
    		}
    		memset(sum,0,sizeof(sum));
    	}
    	printf("%lld\n",ans);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    110pts

    //洛谷 P8313 [COCI2021-2022#4] Izbori
    #pragma GCC optimize(3)
    #include
    #include
    #include
    #include
    #include
    using namespace std;
    int n;
    int a[200010];
    int b[200010];//用于离散化 
    long long d[400010];
    long long e[400010];
    long long f[400010];
    int cnt;
    int x,y;
    long long ans;
    vector<int>t[200010];
    
    int lowbit(int x){
    	return x&-x;
    }
    
    int find(int x){
    	int l=0,r=cnt,mid;
    	while(l<r){
    		mid=(l+r+1)>>1;
    		if(b[mid]<=x){
    			l=mid;
    		}
    		else{
    			r=mid-1;
    		}
    	}
    	return l;
    }
    
    void in(int &x){
    	int nt;
    	x=0;
    	while(!isdigit(nt=getchar()));
    	x=nt^'0';
    	while(isdigit(nt=getchar())){
    		x=(x<<3)+(x<<1)+(nt^'0');
    	}
    }
    
    void add(int i,long long c){
    	int x=i;
    	while(i<=2*n+2){
    		d[i]+=c;
    		e[i]+=c*x;
    		f[i]+=c*x*x;
    		i+=lowbit(i);
    	}
    }
    
    long long query(int i){
    	long long ans=0;
    	int x=i;
    	while(i){
    		ans+=d[i]*(x+2)*(x+1)-e[i]*(2*x+3)+f[i];
    		i-=lowbit(i);
    	}
    	return ans/2;
    }
    
    int main(){
    	register int i,j,l;
    	in(n);
    	for(i=1;i<=n;++i){
    		in(a[i]);
    		b[i]=a[i];
    	}
    	sort(b+1,b+n+1);
    	cnt=unique(b+1,b+n+1)-(b+1);
    	for(i=1;i<=n;++i){
    		t[find(a[i])].push_back(i);
    	}
    	for(i=1;i<=cnt;++i){
    		t[i].push_back(n+1);
    		l=0;
    		for(j=0;j<t[i].size();++j){
    			y=2*j-l+n+1;
    			x=2*j-t[i][j]+n+2;
    			ans+=query(y-1)-(x>=3?query(x-2):0);
    			add(x,1);
    			add(y+1,-1);
    			l=t[i][j];
    		}
    		l=0;
    		for(int j=0;j<t[i].size();++j){
    			y=2*j-l+n+1;
    			x=2*j-t[i][j]+n+2;
    			add(x,-1);
    			add(y+1,1);
    			l=t[i][j];
    		}
    	}
    	printf("%lld\n",ans);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
  • 相关阅读:
    一篇文章入门Word2Vec
    ES7 Nested Sort Search
    《web课程设计》基于HTML+CSS+JavaScript典的中医药大学网(11个页面)
    计算机中的加法器和比较器
    软件测试那十个不为人知的秘密,千万不要被误解了、
    python知识点_初级(汇总)
    Java在互联网网络安全中的应用(三)
    网络体系结构
    C语言实现用递归方法求 () = ∑ (^2)
    Mapbox加载arcgis的底图
  • 原文地址:https://blog.csdn.net/AmyLiu_1020/article/details/127392384