n n n 个人在坐成一排,每个人有一个要装水的时间 t i t_i ti ,每个人装水时间为 p p p ,规定当每个人的装水时间到了,他会先左往右看他左边有没人不在位置上,如果有,那么他不会去装水,等待下一时刻再次判断。如果同一时刻有多个人要去装水,则最左边的优先,问每一个人装完水的时间。
考虑装水的队列,当 p o s i pos_i posi 在队列中,则 大于 p o s i pos_i posi 的人不可能去排队,那么只要每次处理完 p o s i pos_i posi ,查看在其左边的人中找到最小的且小于当前时间的人加入队列即可,相当于排在其后面。那么只要判断一下没有人在队列中的情况,那么就是在剩余所有人中找到时间满足且在最左边的人,可以用线段树上二分来维护。
#include
#define ls x<<1
#define rs x<<1|1
#define ll long long
#define inf 1e18
using namespace std;
const int N=1e5+10;
ll n,p;
struct node{
ll mi,id;
node operator + (const node &t)const{
node temp;temp.mi=min(mi,t.mi);
if(temp.mi==t.mi)temp.id=t.id;
if(temp.mi==mi&&id<t.id)temp.id=id;
return temp;
}
}e[N*4];
ll arr[N],ans[N];
void built(int x,int l,int r){
if(l==r){
e[x].mi=arr[l];e[x].id=l;return ;
}
int mid=(l+r)/2;
built(ls,l,mid);built(rs,mid+1,r);
e[x]=e[ls]+e[rs];
}
void update(int x,int l,int r,int pos,ll val){
if(l==r){
e[x].mi=val;return;
}
int mid=(l+r)>>1;
if(pos<=mid)update(ls,l,mid,pos,val);
else update(rs,mid+1,r,pos,val);
e[x]=e[ls]+e[rs];
}
node query(int x,int l,int r,int LL,int RR){
if(l>=LL&&r<=RR)return e[x];
int mid=(l+r)/2;
if(RR<=mid)return query(ls,l,mid,LL,RR);
else if(LL>mid)return query(rs,mid+1,r,LL,RR);
else return query(ls,l,mid,LL,RR)+query(rs,mid+1,r,LL,RR);
}
node query1(int x,int l,int r,ll val){//线段树二分
if(l==r)return e[x];
int mid=(l+r)/2;
if(e[ls].mi<=val)return query1(ls,l,mid,val);
else return query1(rs,mid+1,r,val);
}
node query2(int x,int l,int r,int LL,int RR,ll val){
if(LL>RR)return (node){-1,-1};
if(l>=LL&&r<=RR){
if(e[x].mi<=val)return query1(x,l,r,val);
else return (node){-1,-1};
}
int mid=(l+r)/2;
node ans=query2(ls,l,mid,LL,RR,val);
if(ans.id==-1)ans=query2(rs,mid+1,r,LL,RR,val);
return ans;
}
queue<int>q;
node temp;
void solve(){
built(1,1,n);ll t=0;
for(int i=1;i<=n;i++){
if(q.empty()){//没有人提前去排队
temp=e[1];
if(temp.mi>t){//无人要装水,时间增加
ans[temp.id]=temp.mi+p;t=ans[temp.id];
update(1,1,n,temp.id,inf);int pos=temp.id;
temp=query(1,1,n,1,pos);
if(temp.mi<t)q.push(temp.id);
continue;
}
temp=query2(1,1,n,1,n,t);//时间小于t且在最左边
ans[temp.id]=t+p;t=t+p;
update(1,1,n,temp.id,inf);int pos=temp.id;
temp=query(1,1,n,1,pos);
if(temp.mi<t)q.push(temp.id);
}else{//有人在队列中先处理
int now=q.front();q.pop();
update(1,1,n,now,inf);t=t+p;ans[now]=t;
temp=query(1,1,n,1,now);
if(temp.mi<t)q.push(temp.id);
}
}
}
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%lld%lld",&n,&p);
for(int i=1;i<=n;i++)scanf("%lld",&arr[i]);
solve();
for(int i=1;i<=n;i++){
printf("%lld%c",ans[i],i==n?'\n':' ');
}
return 0;
}
有 n n n 个人,每个人家有一只猫。每个人都认识一些猫(其中肯定包括自己家的猫)。选出 a a a 个人和 b b b 只猫 ( a , b ≥ 1 ) (a,b≥1) (a,b≥1) 。使得 a + b = n a+b=n a+b=n 且选出的人和猫都互不认识。求方案
可以观察到,这是一个 2 − s a t 2-sat 2−sat 问题,对于给定的每一条认识关系,我们可以通过建边使用强连通分量来解,但是由于本题存在一个特殊的条件,对于每一个人必定认识自家的猫,那么我们假设人用下标 1 1 1 表示,猫用下标 2 2 2 表示可以得到对于 x 1 , x 2 x1,x2 x1,x2两个必须要要选择一个,如若不然因为 a + b = n a+b=n a+b=n 那么必然存在 y 1 , y 2 y1,y2 y1,y2同时选择的情况就出现矛盾。由此一人一猫必选其一,那么如果存在一个认识关系为 ( x 1 , y 2 ) (x1,y2) (x1,y2) ,那么我们假设选择了 x 1 x1 x1 就必然要选择 x 2 x2 x2 ,因为 y 2 y2 y2 不能选,那么我可以枚举选不选第一个人的这两种情况,通过这些关系推出选择方案,选择其中合法的方案即可。 (要注意下标问题,容易弄混)
#include
using namespace std;
const int N=1e6+10;
vector<int>e1[N],e2[N];
int n,m,T,cnt1,cnt2,vis1[N],vis2[N];
void dfs1(int u){
vis1[u]=1;cnt1++;//选择人u
for(int v:e1[u]){
if(vis1[v])continue;
dfs1(v);
}
}
void dfs2(int u){
vis2[u]=1;cnt2++;//选择猫u
for(int v:e2[u]){
if(vis2[v])continue;//连接的v是人,但对应的v猫则必选
dfs2(v);
}
}
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)e1[i].clear(),e2[i].clear(),vis1[i]=vis2[i]=0;
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
if(u!=v)e1[u].push_back(v),e2[v].push_back(u);
}
cnt1=cnt2=0;
dfs1(1);
dfs2(1);
if(cnt1==n&&cnt2==n){
puts("No");continue;
}
puts("Yes");
if(cnt1!=n){
printf("%d %d\n",cnt1,n-cnt1);
for(int i=1;i<=n;i++)if(vis1[i]==1)printf("%d ",i);puts("");
for(int i=1;i<=n;i++)if(vis1[i]!=1)printf("%d ",i);puts("");
continue;
}
if(cnt2!=n){
printf("%d %d\n",n-cnt2,cnt2);
for(int i=1;i<=n;i++)if(vis2[i]!=1)printf("%d ",i);puts("");
for(int i=1;i<=n;i++)if(vis2[i]==1)printf("%d ",i);puts("");
continue;
}
}
return 0;
}