質問の意味:長さnのストリップを黒または白のみで染色し、i番目のブロックを黒または白でそれぞれ染色し、点arr [i]とbrr [i]を追加し、これらの線分に対してm線分を与える同時に、特定の色を染めることは余分なポイントを持っています、最大のボーナスポイントを見つけてください。
分析:競合他社では、明らかに間違った偽のアルゴリズムを使用して中程度の難易度を上回っています。問題のデータが少なすぎて、コードが完全に正しいと感じています(⊙﹏⊙)
中程度の難易度の正しいアルゴリズムについて考えてみてくださいO(n + m ^ 2)は次のとおりです:黒と白の間隔を右から左に向かって小から大に配置し、dpのプロセスで右の境界に達したときにdpを実行します。状態遷移方程式はdp i] = max(dp [i]、dp [j-1] + pre [i] -pre [j-1] + sum)、jはそれぞれの前に実行される線分です(O(m)回必要) sumの左側の境界は、j以上の左側の境界を越えたラインセグメントのプラスポイントの合計であり、右側の項目は、すべてのjからiを特定の色でペイントする最も右側のソリューションを表します。
mが3e5に達しても、黒と白の間隔は引き続き個別に処理されますが、1つずつ計算されるたびにタイムアウトします。したがって、各線分の余分なポイントを使用して、ここで使用される線分ツリーの結果を表す必要があります。線分ツリーの意味は簡単に理解できません。これは、iの変化に伴って常に更新される線分ツリーです。線分ツリーのk番目のノードの値は、kとiの間のすべての色がidとしてペイントされている場合、1からiまでの追加ポイントを表します。最大値idには0と1の2つの状態のみがあり、すべて黒く、すべて白く染色された2つのラインセグメントツリーを表します。
状態遷移方程式や問題解決分析を見てもdp問題を理解するのは難しいといつも感じていましたが、コードを直接見て変換ロジックを理解した方がいいです。。。。
コード:
#include <bits/stdc++.h>
#define x first
#define y second
#define mid (l+r>>1)
#define lo (o<<1)
#define ro (o<<1|1)
using namespace std;
typedef long long ll;
typedef vector<int>vi;
typedef pair<int,int> pii;
struct tri{int x,y,z;ll dp;};
const int inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;
const int maxn=3e5+10;
const ll mod=1e9+7;
const double PI=acos(0)*2;
bool cmp(tri a,tri b)
{
return a.y<b.y;
}
ll arr[maxn],brr[maxn];
int n,m;
vector<tri>sa,sb;
ll ma[2][maxn<<2],lazy[2][maxn<<2];
void build(int o=1,int l=1,int r=n)
{
ma[0][o]=ma[1][o]=-linf;
if(l==r)
{
return;
}
build(lo,l,mid);
build(ro,mid+1,r);
}
void pushdown(int id,int o,int l,int )
{
if(!lazy[id][o])return;
ma[id][lo]+=lazy[id][o];
ma[id][ro]+=lazy[id][o];
lazy[id][lo]+=lazy[id][o];
lazy[id][ro]+=lazy[id][o];
lazy[id][o]=0;
}
void add(int id,int ql,int qr,ll v,int o=1,int l=1,int r=n)
{
if(ql<=l&&r<=qr)
{
ma[id][o]+=v;
lazy[id][o]+=v;
return;
}
pushdown(id,o,l,r);
if(ql<=mid)add(id,ql,qr,v,lo,l,mid);
if(qr>mid)add(id,ql,qr,v,ro,mid+1,r);
ma[id][o]=max(ma[id][lo],ma[id][ro]);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
// freopen("in.txt","r",stdin);
cin>>n>>m;
build();
for(int i=1;i<=n;i++)cin>>arr[i];
for(int i=1;i<=n;i++)cin>>brr[i];
for(int i=0;i<m;i++)
{
int a,b,c,d;
cin>>a>>b>>c>>d;
if(a==1)sa.push_back({b,c,d});
else sb.push_back({b,c,d});
}
sa.push_back({0,n+1,0});
sb.push_back({0,n+1,0});//方便边界处理
sort(sa.begin(),sa.end(),cmp);
sort(sb.begin(),sb.end(),cmp);
auto a=sa.begin(),b=sb.begin();
ll ans=0;
for(int i=1;i<=n;i++)
{
add(0,1,i,arr[i]);//第i个一定是黑,这样才能保证从j到i都是黑
add(1,1,i,brr[i]);
add(0,i,i,linf+ans);//开始时为方便都设成了linf,这里补上去
add(1,i,i,linf+ans);//这里还要加上前i+1个的最优解
while(a->y==i)
{
add(0,1,a->x,a->z);//1到a->x之间任一值到i全是黑,那么就可以额外加分a->z
a++;
}
while(b->y==i)
{
add(1,1,b->x,b->z);
b++;
}
ans=max(ma[0][1],ma[1][1]);//ans代表到i的最右解,相当与dp[i]
}
cout<<ans<<endl;
return 0;
}