P1640 [SCOI2010]连续攻击游戏
以前做这题的时候没啥思路。该多练练找性质的题了。尤其这个性质还这么显然。
考虑环的情况:如果把每一个装备的两个性质连边,任何一个环上的所有点都能取到。
应该不难理解吧。进一步可以发现,从这个环往外扩展,整个联通块都能被取到。
所以只剩下树的情况了。我们惊喜地发现边变少了,可以用二分图做了。
其实可以继续找性质,优化到\(O(n)\),懒得想了。
记得数组要开2倍 别问为什么
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define forg(i,x) for(int i=first[x];i;i=nxt[i])
#define uu unsigned
#define fi first
#define se second
#define od(x) ((x)&1)
#define ev(x) (od(x)^1)
#define mi2(x) (1<<(x))
#define scanf a1234=scanf
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define rint register int
int a1234;
char buf[1<<18],*bufs=buf,*buft=buf;
inline int gc(){
return bufs==buft&&(buft=(bufs=buf)+fread(buf,1,1<<18,stdin)),bufs==buft?-1:*bufs++;
}
inline void xxx(){for(;;);}
const int mxn=1e6+3,mxm=1e4+3;
int n,m;pii a[mxn];
int to[mxn<<1],nxt[mxn<<1],first[mxn],tot=1,dep[mxn];
bool vis[mxn],cd[mxn];
inline void gadd(int x,int y){to[++tot]=y,nxt[tot]=first[x],first[x]=tot;}
inline void dfs1(int x){
if(cd[x])return;cd[x]=1;
forg(i,x)dfs1(to[i]);
}
inline void dfs(int x,int e){
vis[x]=1;
forg(i,x)if(i!=(e^1)){
if(!vis[to[i]]){dep[to[i]]=dep[x]+1,dfs(to[i],i);}
else dfs1(x);
}
}
struct eft{
int mch[mxm],to[mxm<<1],nxt[mxm<<1],first[mxm],tot,nn,vis[mxm];
inline void gadd(int x,int y){to[++tot]=y,nxt[tot]=first[x],first[x]=tot;}
inline void add(int x){++nn;gadd(a[x].fi,nn),gadd(a[x].se,nn);}
inline bool hung(rint x,rint xh){
if(vis[x]==xh)return 0;vis[x]=xh;
forg(i,x){
if(!mch[to[i]]||hung(mch[to[i]],xh))return mch[to[i]]=x,1;
}
return 0;
}
// inline bool work(rint x){return hung(x);}
}gg;
int main(){fre(game);
scanf("%d",&n);for(int i=1,u,v;i<=n;++i)scanf("%d%d",&u,&v),gadd(u,v),gadd(v,u),a[i]=pii(u,v),m=max(m,max(u,v));
for(int i=1;i<=m;++i)if(!vis[i])dfs(i,0);
for(int i=1;i<=n;++i)if(!cd[a[i].fi])gg.add(i);
int ans=0;
for(int i=1;i<=m;++i)if(cd[i]||gg.hung(i,i))++ans;else break;
printf("%d\n",ans);
return 0;
}