写起来很麻烦的一道题。不过思路还算简单。
首先不考虑套娃的情况。len的长度内有a个0和b=len-a个1,最多会产生a*b个01子序列。在范围内可以使用如下的策略进行填充。
(对应代码中的fill函数)从左到右,记录当前剩余未填充的1数量cnt1,还需产生的01序列数量k。
如果k>=cnt1,那么填充一个0,因为后面无论如何分配都一定有cnt1个1,总会产生cnt1个01子序列。
否则填充一个1,使得cnt1--。
容易知道这样肯定是能填充满的。
接下来是套娃的情况。将区域分为左,中,右三个部分,其中中间是之前已经填充好的,左边和右边需要现在填充。
这里没必要计算太复杂的约束,可以以O(n)的效率枚举,一共有x个0和y个1需要分配到左右两边,那么我可以假设左边分配cnt0L个0,从而cnt0R=x-cnt0L,对于1的数量也同样计算。
在上述分配条件下,一定会产生的01子序列如下:
- 原本中间部分就有的01子序列数量
- 左边部分的每个0和中间部分的每个1一定产生01子序列
- 中间部分的每个0和右边部分的每个1一定产生01子序列
- 左边部分的每个0和右边部分的每个1一定产生
有可能产生的01子序列的情况就是左边自己和右边自己。 如果两边各自都把1放到前面0放到后面,就不会产生额外的。其实就转化成了一开始说的不带套娃的子问题。
直接枚举每种分配方案,找到一种合适的就继续跑下一层,找不到合适的直接-1。
#include <bits/stdc++.h> using namespace std; const int N = 1000001; int n,m; int l[N],r[N],a[N],b[N],c[N]; pair<pair<int,int>,int> d[N]; int ans[N]; void fill(int l,int r,int cnt0,int k) { int cnt1 = r-l+1-cnt0; for(int i=l;i<=r;i++) { if(k>=cnt1)k-=cnt1; else ans[i]=1,cnt1--; } } int main() { cin>>n>>m; for(int i=1;i<=m;i++) { cin>>l[i]>>r[i]>>a[i]>>b[i]>>c[i]; d[i].first.first = -l[i]; d[i].first.second = r[i]; d[i].second = i; } sort(d+1,d+m+1); for(int i=1;i<=m;i++) { int p = d[i].second; int pre = d[i-1].second; int prel = l[pre],prer=r[pre]; int lenl = l[pre]-l[p]; int lenr = r[p]-r[pre]; int basea=a[pre],baseb=b[pre],basec=c[pre]; if(i==1)lenl=0,lenr=r[p]-l[p]+1,basea=baseb=basec=0; int cnt0 = a[p] - basea; bool flag = false; for(int x=0;x<=min(cnt0,lenl);x++) { int cnt0l=x,cnt0r=cnt0-x; int cnt1l=lenl-cnt0l; int cnt1r=lenr-cnt0r; int basek = basec+cnt0l*baseb+cnt1r*basea+cnt0l*cnt1r; int addonk=cnt0l*cnt1l+cnt0r*cnt1r; if(cnt1l<0||cnt1r<0)continue; if(c[p]>basek+addonk||c[p]<basek)continue; flag = true; int addon = c[p]-basek; if(addon<=cnt0l*cnt1l) { fill(l[p],l[p]+lenl-1,cnt0l,addon); fill(r[p]-lenr+1,r[p],cnt0r,0); } else { fill(l[p],l[p]+lenl-1,cnt0l,cnt0l*cnt1l); fill(r[p]-lenr+1,r[p],cnt0r,addon-cnt0l*cnt1l); } break; } if(!flag) { cout<<-1; return 0; } } for(int i=1;i<=n;i++)cout<<ans[i]; } // 64 位输出请用 printf("%lld")