传送门
看完题应该都知道是网络流了吧。
但是第二种武器直接建图会gg。
因此我们用线段树优化建图。
具体操作就是,对于这m个人先建一棵线段树,父亲向儿子连容量为inf的边,最后叶子结点向对应的人连容量为1的边。
这样给第二种武器对应连边的时候直接给区间连边就行了。
对于操作三,我们直接贪心流掉两个人,剩下的一个人不流就行了。
代码:
#include
#define N 200005
#define inf 0x3f3f3f3f
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
bool vis[N];
int n,m,id[N],q[N],hd,tl,tot,Begin,ans,d[N],first[N],cnt,s,t;
struct edge{int v,c,next;};
map<int,int>mp[N];
edge e[N];
inline void add(int u,int v,int c){
e[++cnt].v=v,e[cnt].c=c,e[cnt].next=first[u],first[u]=cnt;
e[++cnt].v=u,e[cnt].c=0,e[cnt].next=first[v],first[v]=cnt;
}
inline bool bfs(){
queue<int>q;
q.push(s);
memset(d,-1,sizeof(d));
d[s]=0;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=first[x];i!=-1;i=e[i].next){
int v=e[i].v;
if(e[i].c<=0||d[v]!=-1)continue;
d[v]=d[x]+1;
if(v==t)return true;
q.push(v);
}
}
return false;
}
inline int dfs(int x,int f){
if(!f||x==t)return f;
int flow=f;
for(int i=first[x];i!=-1;i=e[i].next){
int v=e[i].v;
if(flow&&e[i].c>0&&d[v]==d[x]+1){
int tmp=dfs(v,min(flow,e[i].c));
if(tmp==0)d[v]=-1;
flow-=tmp;
e[i].c-=tmp;
e[i^1].c+=tmp;
}
}
return f-flow;
}
inline void solve(){while(bfs())ans+=dfs(s,inf);}
inline void build(int p,int l,int r){
id[p]=++tot;
if(l==r){add(id[p],l,1);return;}
build(lc,l,mid),build(rc,mid+1,r);
add(id[p],id[lc],inf),add(id[p],id[rc],inf);
}
inline void update(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr){add(tot,id[p],1);return;}
if(qr<=mid)update(lc,l,mid,ql,qr);
else if(ql>mid)update(rc,mid+1,r,ql,qr);
else update(lc,l,mid,ql,mid),update(rc,mid+1,r,mid+1,qr);
}
int tmp=0;
inline void query(int p){
if(p>=Begin){tmp=p-Begin+1;return;}
map<int,int>::iterator it=mp[p].begin();
query(it->first);
--it->second;
if(!it->second)mp[p].erase(it);
}
int main(){
memset(first,-1,sizeof(first)),n=read(),tot=m=read(),cnt=-1,s=++tot,build(1,1,m),Begin=tot+1;
for(int i=1;i<=n;++i){
int op=read();
++tot;
if(op==0){
int k=read();
while(k--){
int x=read();
add(tot,x,1);
}
add(s,tot,1);
}
else if(op==1){
int ql=read(),qr=read();
update(1,1,m,ql,qr),add(s,tot,1);
}
else{
int a=read(),b=read(),c=read();
vis[a]=vis[b]=1,ans+=2;
add(tot,a,0),e[cnt].c=1,add(tot,b,0),e[cnt].c=1,add(tot,c,1);
}
}
t=++tot;
for(int i=1;i<=m;++i)if(!vis[i])add(i,t,1);
solve();
printf("%d\n",ans);
for(int i=1;i<=tot;++i)for(int j=first[i];j;j=e[j].next)if((j&1)&&e[j].c)mp[i][e[j].v]=e[j].c;
for(int i=1;i<=m;++i){
bool f=true;
for(int j=first[i];j&&f;j=e[j].next)if(e[j].v==t&&e[j].c)f=false;
if(f)query(i),printf("%d %d\n",tmp,i);
}
return 0;
}