[CF587D] Duff in Mafia
前言
加油啊,不然屯的炉石英文台词在退役前用不完了!
题目
洛谷
CF
讲解
让最大值最小,条件反射直接二分。
随便画画图发现一个点如果有超过两条相同颜色的边就会寄,如果一个点超过两组颜色相同的边也会寄。
剩下的情况就很 2-SAT 了。
如果有且仅有两条颜色相同的边,显然二者必选其一,而其它边不能选。
如果没有,意思是这个点我们能选且仅能选一条边,也就是说我们需要在 2-SAT 的图上每条边选的状态需要连向其它所有边不选的状态,这个可以前缀和优化建图实现。
外面套个二分就做完了,时间复杂度 \(O(n\log_2c)\)。
代码
懒得算空间就随便开了。
//12252024832524
#include
#define TT template
using namespace std;
typedef long long LL;
const int MAXN = 50005 << 3;
int n,m,cnt;
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
int head[MAXN],tot,lst;
struct edge{
int v,nxt;
}e[MAXN<<2];
void Add_Edge(int u,int v){
// printf("ad %d %d\n",u,v);
e[++tot] = edge{v,head[u]};
head[u] = tot;
}
int value[MAXN];
struct Edge{
int clr,val,ID;
bool operator < (const Edge &px)const{
return clr < px.clr;
}
};
vector G[MAXN];
int inv(int x){return x <= m ? x+m : x-m;}
int dfn[MAXN],dfntot,low[MAXN],bl[MAXN],qlt,s[MAXN],tl;
bool ins[MAXN];
void dfs(int x){
s[++tl] = x; ins[x] = 1;
dfn[x] = low[x] = ++dfntot;
for(int i = head[x],v; i ;i = e[i].nxt){
if(!dfn[v = e[i].v]) dfs(v),low[x] = Min(low[x],low[v]);
else if(ins[v]) low[x] = Min(low[x],dfn[v]);
}
if(low[x] == dfn[x]){
++qlt; int v;
do{
ins[v = s[tl--]] = 0;
bl[v] = qlt;
}while(v ^ x);
}
}
bool check(int up){
tot = lst;
for(int i = 1;i <= cnt;++ i) while(head[i] > lst) head[i] = e[head[i]].nxt;
for(int i = 1;i <= m;++ i) if(value[i] > up) Add_Edge(i,inv(i));
for(int i = 1;i <= cnt;++ i) dfn[i] = 0; dfntot = tl = qlt = 0;
for(int i = 1;i <= cnt;++ i) if(!dfn[i]) dfs(i);
for(int i = 1;i <= m;++ i) if(bl[i] == bl[i+m]) return 0;
return 1;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); m = Read(); cnt = m<<1;
for(int i = 1,u,v,clr,val;i <= m;++ i){
u = Read(); v = Read();
clr = Read(); value[i] = val = Read();
G[u].emplace_back(Edge{clr,val,i});
G[v].emplace_back(Edge{clr,val,i});
}
for(int i = 1;i <= n;++ i) sort(G[i].begin(),G[i].end());
for(int i = 1;i <= n;++ i){
int ID = -1;
for(int l = 0,r,len = G[i].size();l < len;l = r+1){
r = l;
while(r+1 < len && G[i][r+1].clr == G[i][l].clr) ++r;
if(r-l+1 >= 3) {printf("No\n");return 0;}
else if(r-l+1 == 2){
if(ID >= 0) {printf("No\n");return 0;}
ID = l;
}
}
if(ID >= 0){
Add_Edge(G[i][ID].ID,inv(G[i][ID+1].ID));
Add_Edge(inv(G[i][ID].ID),G[i][ID+1].ID);
Add_Edge(G[i][ID+1].ID,inv(G[i][ID].ID));
Add_Edge(inv(G[i][ID+1].ID),G[i][ID].ID);
for(int j = 0,len = G[i].size();j < len;++ j)
if(j != ID && j != ID+1) Add_Edge(G[i][j].ID,inv(G[i][j].ID));
}
else{
int len = G[i].size();
for(int j = 0;j < len;++ j){
if(j) Add_Edge(G[i][j].ID,lst);
lst = ++cnt; Add_Edge(lst,inv(G[i][j].ID));
if(j) Add_Edge(lst,lst-1);
}
for(int j = --len;j >= 0;-- j){
if(j < len) Add_Edge(G[i][j].ID,lst);
lst = ++cnt; Add_Edge(lst,inv(G[i][j].ID));
if(j < len) Add_Edge(lst,lst-1);
}
}
}
lst = tot;
if(!check(1e9)) {printf("No\n");return 0;}
int l = 0,r = 1e9,ans = 1e9;
while(l <= r){
int mid = (l+r) >> 1;
if(check(mid)) r = mid-1,ans = mid;
else l = mid+1;
}
check(ans);
printf("Yes\n");
Put(ans,' '); ans = 0;
for(int i = 1;i <= m;++ i) if(bl[i] < bl[i+m]) ++ans;
Put(ans,'\n');
for(int i = 1;i <= m;++ i) if(bl[i] < bl[i+m]) Put(i,' ');
return 0;
}