1. 程式人生 > 其它 >牛客寒假第六場 價值序列

牛客寒假第六場 價值序列

價值序列

題意

給出a1,a2,a3......an各項之差的絕對值之和為M,選出串(1<=i1<=i2<=i3<=i4<=.......in<=n)使得ai1,ai2,ai3,ai4.....aik的各項之間之差的絕對值之和M相等,問有幾種選法。

思路

首先會發現,無論去掉哪個點,都不會使最終的值增加。

把題目理解為去掉幾個點不影響題目的值

1.畫出各點高度的連線圖,發現只要兩個極值點之間的點去掉均不影響,而要留下極值點。

​ 2.考慮特殊情況,在非極值點的位置,若有值相等,這些值都可以去。

​ 3.在極值點的位置,若極值點附近的點和極值點相等,那麼需要留下一個極值點。

​ 4.在起點與終點的位置,若有值相等,無論怎樣都要留下一個值!把其當作一個極值點考慮

我們把點都歸納為(沒有相等的)上升或者下降的,把相同的點的個數賦給第一個出現該點的位置的計數器,這樣我們就把情況簡化到 1

而那麼每個點對題目的貢獻就為 pow(2,num[ i ]),若是極值點,該答案-1,因為要留下一種!

思路

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+7;
const int mod=998244353;
bool vis[N];
int a[N];
int b[N];
int num[N];
int qpow(int x,int n)
{
    int base=1;
    while(n)
    {
        if(n&1)
        {
            base=base*x%mod;
        }
        x=x*x%mod;
        n>>=1;
    }
    return base;
}
void solve()
{
    memset(vis,0,sizeof(vis));
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(num,0,sizeof(num));
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        b[++cnt]=a[i];
        num[cnt]=1;
        while(i<n&&a[i]==a[i+1])
        {
            ++i;
            ++num[cnt];
        }
    }
    vis[1]=vis[cnt]=true;
    for(int i=2;i<=cnt-1;i++)
    {
        if(b[i-1]>b[i]&&b[i]<b[i+1])
        {
            vis[i]=true;
        }
        if(b[i-1]<b[i]&&b[i]>b[i+1])
        {
            vis[i]=true;    
        } 
    }
    long long res=1;
    for(int i=1;i<=n;i++)
    {
        if(vis[i])      //非單調
        {
            res=res*(qpow(2,num[i])-1)%mod; 
        }
        else
        {
            res=res*qpow(2,num[i])%mod;
        }
    }
    cout<<res<<endl;
}
signed main()
{
    int t;
    cin>>t;
    while(t--)
    {
        solve();
    }
}