1. 程式人生 > >【洛谷P4144】大河的序列

【洛谷P4144】大河的序列

二進制 兩個 con 尋找 題目 b- inf getchar end

題目大意:給定一個長度為 N 的序列,求序列中連續區間最大的(或和加與和)是多少。

題解:
引理:任意兩個數 \(i, j\),若 \(i>j\),則在二進制表示下,i 對應的二進制串的字典序一定大於 j 對應的二進制串的字典序。
根據引理,若當前的最優解為 X,現考慮新加入一個元素 Y,有以下三種情況。

  1. \(X>Y\),則 Y 不應加入 X 對答案的貢獻中,因為對於或來說新加入 Y 的貢獻會比 Y & X 對答案的負貢獻小。
  2. \(X=Y\),則無所謂。
  3. \(X<Y\),不妨將 Y 設為當前最優解,結果會變得更優。
    綜上,答案為序列中元素最大值的二倍。

二進制的最優解問題是具有貪心性質的,即:一個高位的 1 比所有低位均為 1 還要大。因此,只需每次盡量使得高位為 1 即可取得最優解。

另一種解法是 貪心+二分,枚舉左端點,再根據二進制位從高到低進行貪心,用二分加速尋找最優的右端點。時間復雜度為 \(O(nlog^2n)\)

代碼如下

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin(),x.end()
#define cls(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int dx[]={0,1,0,-1};
const int dy[]={1,0,-1,0};
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
const double eps=1e-6;
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll sqr(ll x){return x*x;}
inline ll fpow(ll a,ll b,ll c){ll ret=1%c;for(;b;b>>=1,a=a*a%c)if(b&1)ret=ret*a%c;return ret;}
inline ll read(){
    ll x=0,f=1;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
    do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
    return f*x;
}
/*------------------------------------------------------------*/

ll n,b,p,a[maxn],sum[32][maxn];

void read_and_parse(){
    n=read(),b=read(),p=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=0;i<=25;i++)
        for(int j=1;j<=n;j++)
            sum[i][j]=sum[i][j-1]+(a[j]>>i&1);
}
void solve(){
    ll ans=0;
    for(int i=1;i<=n;i++){
        ll lb=i,rb=n,ret=0;
        for(int bit=25;~bit;bit--){
            if(a[i]>>bit&1){
                ll l=lb-1,r=rb;
                while(l<r){
                    int mid=l+r+1>>1;
                    if(sum[bit][mid]-sum[bit][i-1]==mid-i+1)l=mid;
                    else r=mid-1;
                }
                if(l==lb-1)ret+=(1<<bit);
                else ret+=2*(1<<bit),rb=l;
            }else{
                ll l=lb,r=rb+1;
                while(l<r){
                    int mid=l+r>>1;
                    if(sum[bit][mid]-sum[bit][i-1]>0)r=mid;
                    else l=mid+1;
                }
                if(r==rb+1)continue;
                else ret+=(1<<bit),lb=r;
            }
        }
        ans=max(ans,ret);
    }
    printf("%lld\n",fpow(ans+233,b,p));
}
int main(){
    read_and_parse();
    solve();
    return 0;
}

【洛谷P4144】大河的序列