题解:[NOIP 2015 普及组] 求和

题目传送门

题意分析

对于三元组 \((x,y,z)\),显然其对于答案的贡献与 \(y\) 无关,因此可以考虑消去 \(y\)\(y-x=z-y\)\(x+z=2y\),也就是说 \(x,z\) 奇偶性相等。

\(\text{50pts}\)

\(\mathcal O(n^2)\) 枚举 \(x,z\),判断 \(color\) 值是否相等后统计答案即可。

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=100000,M=100000,P=10007;
int n,m,number[N+1],color[M+1];
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",number+i);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",color+i);
	}
	int ans=0;	
	for(int l=1;l<=n;l++){
		for(int r=l+2;r<=n;r+=2){
			if(color[l]==color[r]){
				ans=(ans+1ll*(l+r)*(number[l]+number[r]))%P;
			}
		}
	}
	printf("%d\n",ans);
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}

\(\text{90pts}\)

考虑对每一个格子按照颜色奇偶性进行分类,枚举 \(x\) 的时候枚举 \(z\) 即可。

注意 \(x=z\) 时不要统计,同时会重复统计,答案需要除以 \(2\),模数 \(10007\) 为质数,使用逆元即可。

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=100000,M=100000,P=10007;
int n,m,number[N+1],color[M+1];
vector<int>a[M+1][2]; 
int qpow(int a,int n){
	if(!n){
		return 1;
	}
	int t=qpow(a,n>>1);
	t=1ll*t*t%P;
	if(n&1){
		t=1ll*t*a%P;
	}
	return t;
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",number+i);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",color+i);
		a[color[i]][i&1].push_back(i);
	}
	int ans=0;	
	for(int l=1;l<=n;l++){
		for(int r:a[color[l]][l&1]){
			if(r==l){
				continue;
			}
			ans=(ans+1ll*(l+r)*(number[l]+number[r]))%P;
		}
	}
	printf("%d\n",1ll*ans*qpow(2,P-2)%P);
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}

\(\text{100pts TLE}\)

考虑在根据颜色、奇偶性分类后统计时先二分出 \(x\) 可行的 \(z\) 的左边界,减小常数。

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=100000,M=100000,P=10007;
int n,m,number[N+1],color[M+1];
vector<int>a[M+1][2]; 
int qpow(int a,int n){
	if(!n){
		return 1;
	}
	int t=qpow(a,n>>1);
	t=1ll*t*t%P;
	if(n&1){
		t=1ll*t*a%P;
	}
	return t;
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",number+i);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",color+i);
		a[color[i]][i&1].push_back(i);
	}
	int ans=0;	
	for(int l=1;l<=n;l++){
		int p=upper_bound(a[color[l]][l&1].begin(),a[color[l]][l&1].end(),l)-a[color[l]][l&1].begin();
		for(int r=p;r<a[color[l]][l&1].size();r++){
			ans=(ans+1ll*(l+a[color[l]][l&1][r])*(number[l]+number[a[color[l]][l&1][r]]))%P;
		}
	}
	printf("%d\n",ans);
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}

\(\text{100pts AC}\)

考虑拆式子。

对于 \(x\),令 \(l,r\) 分别表示分类后数组 \(pl\) 中可行 \(z\) 的左右边界。

\(x\) 对于答案的贡献为:

\[\begin{aligned} \sum_{i=l}^r[(x+pl_i)(number_x+number_{pl_i})]&=\sum_{i=l}^r[x\cdot number_x+x\cdot number_{pl_i}+pl_i\cdot number_x+pl_i\cdot number_{pl_i}]\\ &=(r-l+1)(x\cdot number_x)+x\sum_{i=l}^rnumber_{pl_i}+number_x\sum_{i=l}^rpl_i+\sum_{i=l}^rpl_i\cdot number_{pl_i} \end{aligned} \]

则对于每一个 \(pl\),处理出 \(number_{pl_i},pl_i,pl_i\cdot number_{pl_i}\) 的前缀和即可。

注意取模。

时间复杂度:\(\mathcal O(n\log n)\)

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=100000,M=100000,P=10007;
int n,m,number[N+1],color[M+1];
struct node{
	int i;
	int numberSum,plSum,plNumberSum;
	node(){
		i=numberSum=plSum=plNumberSum=0;
	}
};
bool operator <(const int &a,const node &b){
	return a<b.i;
}
vector<node>a[M+1][2];
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",number+i);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",color+i);
		auto &pl=a[color[i]][i&1];
		node x;
		x.i=i;
		if(pl.size()){
			x.numberSum=pl.back().numberSum;
			x.plSum=pl.back().plSum;
			x.plNumberSum=pl.back().plNumberSum;
		}
		x.numberSum=(1ll*x.numberSum+number[i])%P;
		x.plSum=(1ll*x.plSum+i)%P;
		x.plNumberSum=(1ll*x.plNumberSum+1ll*i*number[i])%P;
		pl.push_back(x);
	}
	int ans=0;	
	for(int x=1;x<=n;x++){
		auto &pl=a[color[x]][x&1];
		int l=upper_bound(pl.begin(),pl.end(),x)-pl.begin(),r=pl.size()-1;
		int size=r-l+1;
		ans=(ans+(1ll*size*x%P*number[x]%P+1ll*x*(pl[r].numberSum-pl[l-1].numberSum)%P+1ll*number[x]*(pl[r].plSum-pl[l-1].plSum)%P+1ll*(pl[r].plNumberSum-pl[l-1].plNumberSum)%P))%P;
	}
	printf("%d\n",(ans>0?ans:ans+P));
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-07-21 21:29  TH911  阅读(15)  评论(0)    收藏  举报