省队集训Round2 DAY3

T1

这里写图片描述
这里写图片描述

题解

用splay维护权值有序。每次加入一个人,贪心的选出前i-1个人中要求最小的 vi1 ,判断能否满足,如果能满足就把这 vi1 个人的权值-1,剩下的人不需要他们的支持,那么都赋值成0,并且把这些点移动到平衡树中0所在的位置;如果不能满足就直接在平衡树中加入一个-1.

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 2000003
#define LL long long 
using namespace std;
const LL inf=1e18;
int ch[N][3],fa[N],root,sz; 
int n; LL k,val[N],sum[N],delta[N],cover[N],size[N];
int get(int x)
{
    return ch[fa[x]][1]==x;
}
void change(int now,LL v)
{
    if (!now) return;
    val[now]+=v;
    sum[now]+=size[now]*v;
    if (cover[now]!=-inf) cover[now]+=v;
    else delta[now]+=v;
}
void change1(int now,LL v)
{
    delta[now]=0;  cover[now]=v;
    val[now]=v; sum[now]=v*size[now]; 
}
void pushdown(int now)
{
    if (delta[now]) {
        change(ch[now][0],delta[now]);
        change(ch[now][1],delta[now]);
        delta[now]=0;
    }
    if (cover[now]!=-inf) {
        change1(ch[now][0],cover[now]);
        change1(ch[now][1],cover[now]);
        cover[now]=-inf;
    }
}
void update(int now)
{
    int l=ch[now][0]; int r=ch[now][1];
    sum[now]=val[now]; size[now]=1;
    if (l) sum[now]+=sum[l],size[now]+=size[l];
    if (r) sum[now]+=sum[r],size[now]+=size[r];
}
void rotate(int x)
{
    int y=fa[x]; int z=fa[y]; int which=get(x);
    pushdown(y); pushdown(x);
    ch[y][which]=ch[x][which^1]; fa[ch[x][which^1]]=y;
    ch[x][which^1]=y; fa[y]=x; fa[x]=z;
    if (z) ch[z][ch[z][1]==y]=x;
    update(y); update(x);
}
void splay(int x,int tar)
{
    for (int f;(f=fa[x])!=tar;rotate(x))
     if (fa[f]!=tar) rotate(get(x)==get(f)?f:x);
    if (!tar) root=x;
}
void insert(LL x)
{
    int now=root;
    while (true) {
        pushdown(now);
        int f=now;
        now=ch[now][x>val[now]];
        if (!now) {
            ++sz; sum[sz]=val[sz]=x; size[sz]=1;
            ch[f][x>val[f]]=sz; fa[sz]=f; cover[sz]=-inf;
            update(f); splay(f,0); 
            return;
        }
    }
}
int find(int x)
{
    int now=root;
    while (true) {
        pushdown(now);
        if (size[ch[now][0]]>=x) now=ch[now][0];
        else {
            x-=size[ch[now][0]];
            if (x==1) return now;
            x--; now=ch[now][1];
        }
    }
}
int main()
{
    freopen("yi.in","r",stdin);
    freopen("yi.out","w",stdout);
    scanf("%d%lld",&n,&k);
    root=1; sz=2;
    fa[2]=1; ch[1][1]=2; cover[1]=cover[2]=-inf;
    val[1]=-inf; sum[1]=0; val[2]=sum[2]=inf; size[1]=2; size[2]=1;
    for (int i=1;i<=n;i++) {
        int x; scanf("%d",&x);
        if (size[root]-2<x-1){
            insert(-1);
            printf("-1\n");
            continue;
        }
        int aa=find(1); int bb=find(x+1);
        splay(aa,0); splay(bb,aa);
        int t=ch[ch[root][1]][0];
        if (sum[t]+x-1>k) {
            insert(-1);
            printf("-1\n");
            continue;
        }
        if (t) {
            val[t]++; sum[t]+=size[t];
            if (cover[t]!=-inf) cover[t]++;
            else delta[t]++;
        }
        LL v=k-sum[t];
        aa=find(x); bb=find(size[root]);
        splay(aa,0); splay(bb,aa);
        t=ch[ch[root][1]][0];
        if (t) {
            change1(t,0);
            ch[ch[root][1]][0]=0; fa[t]=0; update(ch[root][1]); update(root);
            aa=find(1); bb=find(2);
            splay(aa,0); splay(bb,aa);
            ch[ch[root][1]][0]=t; fa[t]=ch[root][1];
            update(ch[root][1]); update(root);
        }
        insert(v);
        printf("%lld\n",v);
    }
}


T3

这里写图片描述
这里写图片描述

题解

要求每一个时间每条边只能有一个人经过,每个人在到达终点前的任意时刻都不能停止。
那么我们可以二分一个最晚的时间mid,然后建mid层结点,每条有向边x->y从i层的x结点连向i+1层的y结点,容量为1。然后跑最大流如果最后满流,那么说明mid可行,继续减小二分的范围

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define N 3000003
#define inf 1000000000
using namespace std;
int n,m,k,tot;
int point[N],nxt[N],remain[N],v[N],cur[N],last[N],deep[N],num[N];
int x[20000],y[20000];
void add(int x,int y,int z)
{
   tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
   tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
   //cout<<x<<" "<<y<<" "<<z<<endl;
}
int addflow(int s,int t)
{
   int ans=inf; int now=t;
   while (now!=s) {
     ans=min(ans,remain[last[now]]);
     now=v[last[now]^1];
   }
   now=t;
   while (now!=s) {
     remain[last[now]]-=ans;
     remain[last[now]^1]+=ans;
     now=v[last[now]^1];
   }
   return ans;
}
void bfs(int s,int t)
{
    for (int i=1;i<=t;i++) deep[i]=t;
    queue<int> p;
    p.push(t); deep[t]=0;
    while (!p.empty()){
      int now=p.front(); p.pop();
      for (int i=point[now];i!=-1;i=nxt[i])
       if (deep[v[i]]==t&&remain[i^1])
        deep[v[i]]=deep[now]+1,p.push(v[i]);
    }
}
int isap(int s,int t)
{
   int ans=0; int now=s;
   bfs(s,t);
   for (int i=1;i<=t;i++) num[deep[i]]++;
   for (int i=1;i<=t;i++) cur[i]=point[i];
   while (deep[s]<t){
      if (now==t) {
       ans+=addflow(s,t);
       now=s;
      }
      bool pd=false;
      for (int i=cur[now];i!=-1;i=nxt[i]) 
       if (remain[i]&&deep[now]==deep[v[i]]+1) {
          cur[now]=i;
          last[v[i]]=i;
          pd=true;
          now=v[i];
          break;
       }
      if (!pd) {
         int minn=t+1;
         for (int i=point[now];i!=-1;i=nxt[i])
          if (remain[i]) minn=min(minn,deep[v[i]]);
         if (!--num[deep[now]]) break;
         num[deep[now]=minn+1]++;
         cur[now]=point[now];
         if (now!=s) now=v[last[now]^1];
      }
    }
   return ans;
}
bool check(int mid)
{
    tot=-1;
    int S=n*mid+1; int T=n*mid+2;
    for (int i=1;i<=T;i++) num[i]=0;
    for (int i=1;i<=T;i++) point[i]=-1;
    add(S,1,k); 
    for (int i=1;i<=mid;i++)
     add(i*n,T,k);
    for (int i=1;i<=m;i++) 
      for (int j=1;j<mid;j++)
       add((j-1)*n+x[i],j*n+y[i],1);
    if (isap(S,T)==k) return true;
    return false;
}
int main()
{
    freopen("san.in","r",stdin);
    freopen("san.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]);
    int l=0; int r=n+k+2; int ans=n+k+2;
    while (l<=r) {
      int mid=(l+r)/2;
      if (check(mid)) ans=min(ans,mid),r=mid-1;
      else l=mid+1;
    }
    if (ans==n+k+2) printf("-1\n");
    else printf("%d\n",ans-1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值