Bootstrap

Codeforces Round #708 (Div. 2)

D题

题意:有n个问题,编号是1到n,第i个问题由复杂程度是Ci=2i,标签tagi和si组成。
解完问题i后,允许解问题j当且仅当IQ<|ci−cj|且tagi≠tagj。解完后,你的IQ改变为IQ=|ci−cj|,你得到|si−sj|分数。
任何问题都可以是第一个。你可以按照任何顺序,任意次数地解决问题。
最初你的IQ= 0。找出可以获得的最大点数。

思路:动态规划,考虑一个图,其中顶点是问题,顶点i和顶点j之间有一条边{i,j},权值|ci−cj|。每条边都有一个唯一的权值。让我们证明。
假设weight=|2i−2j|, i>j。那么在二进制形式中,当且仅当j≤k<i时,weight的第k位二进制位为真。那么对于每个唯一的一对{i,j},权值也是唯一的,因为一个子段的二进制位为真是唯一的。
设dpi为问题i结束时可获得的最大点数。初始时,每1≤i≤n时,dpi=0。让我们以递增的顺序来考虑所有的边(因为IQ应该增加)。为了做到这一点,我们可以考虑j从2增加到n然后i从j−1减少到1。它也可以用权值的二进制形式来解释。
现在让我们松弛dp值。当考虑边{i,j},tagi≠tagj时,我们尝试求解以j结尾的dpj问题后的问题i,求解以i结尾的dpi问题后的问题j,即dpi=max(dpi,dpj+p),dpj=max(dpj,dpi+p),其中p=|si−sj|。考虑所有边后,答案是dp值中的最大值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e5+5;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;cin>>t;
    while(t--){
        int n;cin>>n;
        vector<int>tag(n);
        vector<ll>s(n);
        for(int i=0;i<n;i++)cin>>tag[i];
        for(int i=0;i<n;i++)cin>>s[i];

        vector<ll>dp(n,0);
        for(int j=1;j<n;j++){
            for(int i=j-1;i>=0;i--){
                if(tag[i]==tag[j])continue;
                ll dpi=dp[i];
                ll dpj=dp[j];
                ll p=abs(s[i]-s[j]);
                dp[i]=max(dp[i],dpj+p);
                dp[j]=max(dp[j],dpi+p);
            }
        }
        cout<< *max_element(dp.begin(),dp.end())<<endl;
    }
    return 0;
}

;