【前缀和】
【前缀和】
难点在于推公式
构造矩形
https://ac.nowcoder.com/acm/contest/102742/E
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=1e5+10;
/*
【前缀和推公式】
考虑(a[j]-a[i])作为长和作为宽的情况
作为长:宽的个数应该是m-(a[j]-a[i]-k)+1
【将i固定】
->考虑左右边界:(a[j]-a[i])>k 且 m-(a[j]-a[i]-k)+1>0 ->j同时有左边界和右边界
作为宽:长的个数应该是m-(a[j]-a[i]+k)+1
->考虑左右边界:m-(a[j]-a[i]+k)+1>0->j有右边界
->①二分找左右边界
->可前缀和优化求和公式:降一维复杂度
*/
int n,m;
ll k;
ll a[N];
ll s[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
ll ans=0;
for(int i=1;i<n;i++){
//作为长
//找左边界
int l=i+1,r=n+1;
while(l<r){
int mid=(l+r)>>1;
if(a[mid]-a[i]>k) r=mid;
else l=mid+1;
}
if(l<n+1){
//找右边界
int L=r;
l=i;r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(m-(a[mid]-a[i]-k)+1>0) l=mid;
else r=mid-1;
}
if(r>i){
int R=l;
ans+=(ll)m*(R-L+1)-(((s[R]-s[L-1])-a[i]*(R-L+1))-k*(R-L+1))+(R-L+1);
//cout<<L<<" "<<R<<endl;
}
}
//作为宽
l=i;r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(m-(a[mid]-a[i]+k)+1>0) l=mid;
else r=mid-1;
}
if(r>i){
int cnt=l;
//cout<<cnt<<endl;
//注意要防止爆int
ans+=(ll)m*(cnt-i)-(((s[cnt]-s[i])-a[i]*(cnt-i))+k*(cnt-i))+(cnt-i);
}
}
cout<<ans;
return 0;
}
异或和之和
https://www.lanqiao.cn/problems/3507/learning/
前缀异或:拆开每一位看贡献
/*【前缀和优化】
ll sum=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
sum+=s[i]^s[j-1];
}
}
->有没有办法去掉一维?
->位运算:考虑枚举每一位
->在每一位下求前缀异或:选择i和j-1,只有0和1组合才能产生贡献
->统计每一位下0和1的个数->乘法原理
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n;
int a[N];
int s[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ll ans=0;
for(int k=0;k<=20;k++){
s[0]=0;
int zero=1,one=0;//初始为0
for(int i=1;i<=n;i++){
int t=(a[i]>>k)&1;
s[i]=s[i-1]^t;
if(s[i]==1) one++;
else zero++;
}
ll res=(ll)one*zero*(1<<k);
ans+=res;
}
cout<<ans;
return 0;
}
卡牌游戏
https://qoj.ac/contest/2182/problem/12369
题目大意
奇数位置和偶数位置上的数的和取最小值
现可以选择一个位置,把他插到其他位置,位置奇偶性会改变
问最后取值的最大值是多少
思路

代码
int n;
void solve(){
cin>>n;
vector<i64> a(2*n+1,0);
for(int i=1;i<=2*n;i++) cin>>a[i];
vector<vector<i64>> s(2,vector<i64>(2*n+1,0));
set<i64> st[2];
//求奇偶前缀和
for(int i=1;i<=2*n;i++){
s[0][i]=s[0][i-1]+((i%2==0)?a[i]:0);
s[1][i]=s[1][i-1]+((i%2==1)?a[i]:0);
}
i64 ans=min(s[0][2*n],s[1][2*n]),d=(s[0][2*n]-s[1][2*n])/2;
//目标是能找到s[0][2*n]-d,s[1][2*n]+d
for(int i=1;i<=2*n;i++){
i64 tar=s[0][i]-s[1][i]-d,v=i%2;//v奇偶性 tar是要在set里找的值
//找接近的:set找最大的最小,最小的最大
auto t=st[v].lower_bound(tar);
if(t!=st[v].end()){
i64 res=*t;
i64 now=s[0][i]-s[1][i]-res;
ans=max(ans,min(s[0][2*n]-now,s[1][2*n]+now));
}
if(st[v].size() && t!=st[v].begin()){
--t;
i64 res=*t;
i64 now=s[0][i]-s[1][i]-res;
ans=max(ans,min(s[0][2*n]-now,s[1][2*n]+now));
}
tar=s[0][i]-s[1][i]-(d+1),v=i%2;
t=st[v].lower_bound(tar);
if(t!=st[v].end()){
i64 res=*t;
i64 now=s[0][i]-s[1][i]-res;
ans=max(ans,min(s[0][2*n]-now,s[1][2*n]+now));
}
if(st[v].size() && t!=st[v].begin()){
--t;
i64 res=*t;
i64 now=s[0][i]-s[1][i]-res;
ans=max(ans,min(s[0][2*n]-now,s[1][2*n]+now));
}
tar=s[0][i]-s[1][i]-(d-1),v=i%2;
t=st[v].lower_bound(tar);
if(t!=st[v].end()){
i64 res=*t;
i64 now=s[0][i]-s[1][i]-res;
ans=max(ans,min(s[0][2*n]-now,s[1][2*n]+now));
}
if(st[v].size() && t!=st[v].begin()){
--t;
i64 res=*t;
i64 now=s[0][i]-s[1][i]-res;
ans=max(ans,min(s[0][2*n]-now,s[1][2*n]+now));
}
st[(i-1)%2].insert(s[0][i-1]-s[1][i-1]);
}
cout<<ans<<endl;
}

浙公网安备 33010602011771号