【前缀和】

【前缀和】

难点在于推公式

构造矩形

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

题目大意

奇数位置和偶数位置上的数的和取最小值
现可以选择一个位置,把他插到其他位置,位置奇偶性会改变
问最后取值的最大值是多少

思路

image

代码

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;
}
posted @ 2025-03-08 18:05  White_ink  阅读(10)  评论(0)    收藏  举报