C.花火を見るのは楽しい(単調キュー最適化dp)

トピックポータル

質問:長さnのストリートにm個の花火ショーがあり、それぞれ時間t [i]で上演されます。位置xに立っていて、i番目の花火ショーを見ると、b [i]-を取得できます。 abs(a [i] -x)は幸福の値であり(それを見るしかないのです!)、いつでもd単位の長さを動かすことができます。最初はどの位置にいてもできます。通りから出て、幸福の価値をどれだけ得ることができるか尋ねてください。(マイナスになる場合があります)。

データ範囲:
1≤n≤150000;1≤m≤300;1≤d≤n1≤n≤150000;1≤m≤300;1≤d≤n1n1 5 0 0 0 0 ;1m3 0 0 ;1dnは
1≤N≤愛。1≤bi≤109; 1≤ti≤1091≤a_i≤n; 1≤b_i≤10^ 9; 1≤t_i≤10^ 91an ;1b1 09 ;1t1 09
t [i]が小さいものから大きいものへと指定されていることを確認します(役に立たない状態のようです。並べ替えるだけです)

解決策:このデータ範囲を見ると、f [i] [j] f [i] [j]を使用することを確実に考えることができますf [ i ] [ j ]は、位置jに立っており、i番目の花火を見たときに得られる最大値を意味します。答えはmax(f [m] [j])、j∈(1 、n)max(f [m] [j])、j∈(1、n)m a x f [ m ] [ j ] j1 n

ただし、花火のi番目の表示はi-1回の影響のみを受けることがわかっているため、1次元空間を最適化できます。つまり、式f [j] = max(f [k] )+ b [i] − abs(a [i] − j)、(k∈(max(1、j −(t [i] − t [i − 1])∗ d)、min(n、j + (t [i] − t [i − 1])∗ d)))f [j] = max(f [k])+ b [i] -abs(a [i] -j)、(k∈( max(1、j-(t [i] -t [i-1])* d)、min(n、j +(t [i] -t [i-1])* d)))f [ j ]=m a x f [ k ] +b [ i ]a b s a [ i ]j km a x 1 jt [ i ]t [ i1 ] d m i n n j+t [ i ]t [ i1 ] d ))

これは、間隔kの最大値が要求されるたびに、RMQを維持するためではありませんか

線分ツリーを使用してRMQを維持する場合、この問題の複雑さはO(nmlog(n))O(nmlog(n))です。O n m l o g n 、残念ながらタイムアウトしました

しかし、通りを横断するときに、以前に使用され、将来再び使用できない情報が破棄される可能性があることがわかりました。これにより、単調なキューを使用して、間隔(スライディングウィンドウ)の最適な値を維持できます。

コード:

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
//#define int long long
#define pii pair<int,int>
#define ull unsigned long long
#define pdd pair<double,double>
#define lowbit(x) x&-x
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read()
{
    
    
    int x=0,f=1;
    char ch=gc();
    while(ch<'0'||ch>'9')
    {
    
    
        if(ch=='-')
            f=-1;
        ch=gc();
    }
    while(ch>='0'&&ch<='9')
    {
    
    
        x=x*10+ch-'0';
        ch=gc();
    }
    return x*f;
}
using namespace std;
const int N=150000+1500;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const double eps=1e-6;
const double PI=acos(-1);
int a[N],b[N],c[N];
ll f[2][N],q[N];
signed main()
{
    
    
    int n=read(),m=read(),d=read();
    for(int i=1; i<=m; i++)
    {
    
    
        a[i]=read();
        b[i]=read();
        c[i]=read();
    }
    int fg=0,t=c[1];
    for(int i=1; i<=m; i++)
    {
    
    
        int l=0,r=0,k=1;
        if(i==1)
        {
    
    
            for(int j=1; j<=n; j++)
                f[fg][j]=b[i]-abs(a[i]-j);//刚开始,直接给值
        }
        else
        {
    
    
            for(int j=1; j<=n; j++)
            {
    
    
                while(k<=n&&k<=j+1LL*(c[i]-c[i-1])*d)//k在合法范围内
                {
    
    
                    while(l<r&&f[fg^1][k]>=f[fg^1][q[r-1]])
                    //后面到的比前面的更大,就一定更优,直接插队就行了
                        r--;
                    q[r++]=k++;
                }
                while(l<r&&j-1LL*(c[i]-c[i-1])*d>q[l])
                //已经超出范围的无用信息就直接删去,最后队列首部就是当前最优的状态
                    l++;
                f[fg][j]=f[fg^1][q[l]]+b[i]-abs(a[i]-j);
            }
        }
        fg^=1;
    }
    ll res=-1e18;
    for(int i=1;i<=n;i++)
        res=max(res,f[fg^1][i]);
    cout<<res<<endl;
    return 0;
}

おすすめ

転載: blog.csdn.net/Joker_He/article/details/109302098