AtCoder Beginner Contest 216 G - 01Sequence (并查集 贪心 树状数组 差分约束)
原创
©著作权归作者所有:来自51CTO博客作者mb62cff40cc1a13的原创作品,请联系作者获取转载授权,否则将追究法律责任
题意
对于每个要求都满足中的个数
构造出这个序列满足上述要求,并且的个数最小。
思路1:
先按照右端点排序,贪心的考虑,从右端点开始填,并且用并查集找下一个可以填的位置。
每次询问的区间和,如果已经的话就不用再考虑这个区间了。
单点修改区间求和可以用树状数组,时间复杂度;
代码1:
const int maxn=2e5+100;
int n,m,tr[maxn],root[maxn],ans[maxn];
struct node{
int l,r,num;
}q[maxn];
bool cmp(node a,node b){
return a.r<b.r;
}
int Find(int x){
if(x!=root[x]) root[x]=Find(root[x]);
return root[x];
}
int lowbit(int x){return x&-x;}
void update(int pos){
while(pos<=n) tr[pos]++,pos+=lowbit(pos);
}
int query(int pos){
int res=0;
while(pos) res+=tr[pos],pos-=lowbit(pos);
return res;
}
int main() {
n=read,m=read;
rep(i,1,m){
q[i].l=read,q[i].r=read,q[i].num=read;
}
sort(q+1,q+1+m,cmp);
rep(i,1,n) root[i]=i;
rep(i,1,m){
int now=query(q[i].r)-query(q[i].l-1);
if(now>=q[i].num) continue;
q[i].num-=now;
for(int j=Find(q[i].r);j>=q[i].l&&q[i].num;j=Find(j)){
update(j);root[j]=Find(j-1);q[i].num--;ans[j]=1;
}
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
return 0;
}
思路2
设表示序列中的的个数,给出的条件就可以转化成,,这样就变成经典的差分约束问题了,由于求的是的最小值,从连一条权值为的边,跑最长路。由于边权为正,所以不能用,又会被卡。
所以就转化成的个数后跑最短路,用,注意要从开始。
代码2
int n,m,dis[maxn];
vector<PII>g[maxn];
bool st[maxn];
void dij(){
memset(dis,0x3f,sizeof dis);
dis[0]=0;
///建立一个维护最小值的优先队列
priority_queue<PII,vector<PII>,greater<PII>>heap;
heap.push({0,0});///起始点放入队列
while(heap.size()){
PII t=heap.top();///最小值
heap.pop();
int ver=t.second,d=t.first;
if(st[ver]) continue;///该点更新
st[ver]=true;
for(int i=0;i<g[ver].size();i++){
int j=g[ver][i].first,w=g[ver][i].second;
if(dis[j]>d+w){
dis[j]=d+w;
heap.push({dis[j],j});
}
}
}
}
int main(){
n=read,m=read;
rep(i,1,m){
int l=read,r=read,x=read;
x=r-l+1-x;
g[l-1].push_back({r,x});
}
rep(i,0,n-1){
g[i].push_back({i+1,1});
g[i+1].push_back({i,0});
}
dij();
rep(i,1,n) printf("%d ", (dis[i]-dis[i-1])^1);
return 0;
}