[线段树 & 前缀 优化建图 二分 2-SAT] CF Gym100159 facebook-hacker-cup-2012 I. Unfriending

22 篇文章 0 订阅
9 篇文章 0 订阅

二分答案

对于一个点,肯定有一个区间里的点和它不能共存

这个可以用线段树优化建图,每个friend list最多只能删一个,用前缀优化建图

还要一些针对随机数据什么的trick才能过…比如二分中的那个特判

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>

using namespace std;

const int N=5000010;

int T,n,m,cnt,tot;
int G[N],vis[N],dfn[N],low[N],s[N],g[N],vst[N],ig,tp,it,imax;
struct edge{
    int t,nx;
}E[N<<1];

pair<int,int> a[N];

inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void rea(int &x){
    char c=nc(); x=0;
    for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

vector<pair<int,int> > b[N];

inline void addedge(int x,int y){
    //cout<<x<<' '<<y<<endl;
    E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
}

void Build(int g,int l,int r){
    if(l==r) return imax=max(imax,g),addedge(tot+g,a[l].second<<1|1);
    int mid=l+r>>1;
    Build(g<<1,l,mid); Build(g<<1|1,mid+1,r);
    addedge(tot+g,tot+(g<<1)); addedge(tot+g,tot+(g<<1|1));
}

void link(int g,int L,int R,int l,int r,int x){
    if(L==l && r==R) return addedge(x,tot+g);
    int mid=L+R>>1;
    if(r<=mid) link(g<<1,L,mid,l,r,x);
    else if(l>mid) link(g<<1|1,mid+1,R,l,r,x);
    else link(g<<1,L,mid,l,mid,x),link(g<<1|1,mid+1,R,mid+1,r,x);
}

void tarjan(int x){
    dfn[x]=low[x]=++it; vis[x]=1; s[++tp]=x;
    for(int i=G[x];i;i=E[i].nx){
        if(!vis[E[i].t]) tarjan(E[i].t);
        if(vis[E[i].t]!=2) low[x]=min(low[x],low[E[i].t]);
    }
    if(dfn[x]==low[x]){
        int k; ++ig;
        do{ k=s[tp--]; vis[k]=2; g[k]=ig; }while(k!=x && tp);
    }
}

inline bool check(int x){
    memset(G,0,sizeof(G));
    memset(vis,0,sizeof(vis));
    cnt=tp=ig=it=imax=0; tot=(n-1<<1|1)+1;
    Build(1,0,n-1);
    int cc=0;
    for(int i=0;i<n;i++)
        if(!vst[i]) addedge(i<<1|1,i<<1);
    for(int i=0,j=0;i<n;i++){
        while(a[i].first-a[j].first>=x) j++;
        if(j<i) link(1,0,n-1,j,i-1,a[i].second<<1),cc+=i-j;
    }
    if(cc>=1200000) return false;
    for(int i=n-1,j=n-1;~i;i--){
        while(a[j].first-a[i].first>=x) j--;
        if(j>i) link(1,0,n-1,i+1,j,a[i].second<<1);
    }
    tot+=imax+1;
    for(int i=1;i<=m;i++){
        addedge(b[i][0].second<<1|1,tot);
        addedge(tot+1,b[i][0].second<<1);
        for(int j=1;j<b[i].size();j++){
            addedge(tot+(j-1<<1),tot+(j<<1));
            addedge(tot+(j<<1|1),tot+(j-1<<1|1));
            addedge(b[i][j].second<<1|1,tot+(j<<1));
            addedge(tot+(j<<1|1),b[i][j].second<<1);
            addedge(b[i][j].second<<1|1,tot+(j-1<<1|1));
            addedge(tot+(j-1<<1),b[i][j].second<<1);
        }
        tot+=(b[i].size()<<1|1)+1;
    }
    for(int i=0;i<=tot;i++)
        if(!vis[i]) tarjan(i);
    for(int i=0;i<n;i++)
        if(g[i<<1]==g[i<<1|1]) return false;
    return true;
}

int main(){
    rea(T); int CASE=0;
    while(T--){
        memset(vst,0,sizeof(vst));
        rea(n); rea(m); rea(a[0].first);
        int x,y,p; rea(x); rea(y); rea(p);
        for(int i=1;i<n;i++) 
            a[i].first=(1LL*a[i-1].first*x+y)%p;
        for(int i=0;i<n;i++) 
            a[i].second=i;
        for(int i=1;i<=m;i++){
            int size,x,y; rea(size);
            b[i].resize(size);
            rea(b[i][0].second); rea(x); rea(y);
            b[i][0].second%=n;
            for(int j=1;j<size;j++) 
                b[i][j].second=(1LL*b[i][j-1].second*x+y)%n;
            for(int j=0;j<size;j++) 
                b[i][j].first=a[b[i][j].second].first,vst[b[i][j].second]=1;
            sort(b[i].begin(),b[i].end());
            b[i].resize(unique(b[i].begin(),b[i].end())-b[i].begin());
        }
        sort(a,a+n);
        int l=0,r=1e9,mid,ans=-1;
        while(l<=r){
            mid=l+r>>1;
            if(l<10 && r>10) mid=10;
            check(mid)?l=(ans=mid)+1:r=mid-1;
        }
        printf("Case #%d: %d\n",++CASE,ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值