星屑幻想 optimal mark

LINK :SP839

星屑幻想 取自 OJ 的名称 小事情...题目大意还是要说的这道题比较有意思,想了一段时间。

给你一张图 这张图给答案带来的贡献是每条边上两个点值得异或 一些点的值已经被确定 如何安排剩下的点的权值使答案最小,求在最小答案的基础上那些未标记的点的权值,如果有多组答案取所有星星威力和最小的。

这道题看似很不可做因为 不被确定的点的个数很多 值我们也不好确定 爆搜直接GG。那么怎么办呢?我的思路是:遇到异或 那就是位与位之间的关系了 先考虑拆位。那么我们至少多了一个logn的复杂度了。

考虑现在是一堆点 某些点的当前这位0 1 已经确定我们如何安排剩余的点0还是1使当前这位答案最小呢?(看起来还是一个爆搜...但确实复杂度比刚才低很多但是这不能解决问题。

其实考虑到这里就已经结束了 点的选择只有两个考虑直接网络流,这其实就转化到了使某个点为0/1使和它相连的点的冲突最小(印象中做过这道题 

由于要求最小 所以最大流貌似解决不了什么大问题 转最小割 设源点为选1 汇点为选0 那么对于一个未赋值的点来说 既连源点也连汇点。注意这里两个点都未赋值且之间有连边很容易让人想到两个属于同一个集合会带来什么什么样的代价 这时虚设 点 最小割之经典可是在这道题却行不通两个点分属不同的集合才会带来代价。这里可能就不太好想了我们先从简单的来判断 一个未赋值的点和一个点当前这位有确定值的,怎么连边可以体现出来这一点。显然的是如果这个点已被确定为1 那么其连向源点流量INF(确保不被割掉)不连汇点 那么这个确定的和不确定的怎么连边才能体现出来如果分属不同集合的话会带来一些代价呢。

其实画个图观察 发现如果当前未知点选择了0 那么S到它就会被割掉 此时已确定点就要发挥出作用再扣留一个代价了 好了说出做法其实就是已确定点再向未确定点连一条流量为1的边保证这条边也被割掉从而累加代价。

那么考虑两个未知的点的时候吧...关键在此 惊喜的发现和上述方法一样 故本题得到初步的解决最小代价求出来了 那么点的价值相信我们便利残余网络都可以求出吧...

听书上说的话 算法的思维程度远比学几个可以直接来解决问题的数据结构重要。

一个比较重要的点是双向边 问题 这个细节 必须注意 两个未明确的点之间 正反边流量都得是1 这样才能更好的判断出谁是割边当然这对最大流==最小割是没有影响的。

码了大概1h debug 2h 这个最小割有点核心啊,太妙了 被卡的地方是遍历残余网络这一部 我以为可以随便写 yy了一个错误的做法一直不知道怎么改。最正确的做法是这样的:书上提到 割边是这样求出的 从S 开始 遍历然后把所有能到的点给标记 不能到的不标记。这样 标记点到没有标记点之间就是割边了,非常的巧妙。。。 这样我们顺理成章的有了一个做法 可以发现被标记点都是属于S 没被标记的点选择了T 那么 01 自然就划分出来了,我原本的做法是基于点来做的 遍历每个点 与源点之间的连接关系 但是遇到的麻烦是两个未确定的点之间的连边流量是无法快速得出关系的此时必须使用上述的做法 由起点开始标记。

code luogu 多组数据的code 时间复杂度 n^2m*30 看起来稳稳的挂可实际上远远达不到这个上界 所以跑的飞快。

//#include<bits/stdc++.h>
#include<iostream>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<utility>
#include<vector>
#include<iomanip>
#include<cstdlib>
#define INF 1000000000
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define RE register
#define EPS 1e-8
#define ll long long
#define ull unsigned long long
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=3010,maxn=520,MAX=MAXN<<2;
int G,n,m,k,S,T,maxflow,flow,len,t,h;
int f[maxn],flag[maxn],mark[MAXN];
int lin[MAX],ver[MAX],nex[MAX],e[MAX],q[MAX],vis[MAX];
struct wy{int x,y;}s[MAXN];
inline void add(int x,int y,int z,int z1)
{
    ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;
    ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=z1;
}
inline void swap(int &x,int &y){int tmp=x;x=y;y=tmp;}
inline int bfs()
{
    t=h=0;
    memset(vis,0,sizeof(vis));
    q[++t]=S;vis[S]=1;
    while(h++<t)
    {
        int x=q[h];
        for(int i=lin[x];i;i=nex[i])
        {
            int tn=ver[i];
            if(!e[i])continue;
            if(vis[tn])continue;
            vis[tn]=vis[x]+1;
            q[++t]=tn;
            if(tn==T)return 1;
        }
    }
    return 0;
}
inline int dinic(int x,int flow)
{
    if(x==T)return flow;
    int rest=flow,k;
    for(int i=lin[x];i&&rest;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn]==vis[x]+1&&e[i])
        {
            k=dinic(tn,min(e[i],rest));
            if(!k){vis[tn]=0;continue;}
            e[i]-=k;e[i^1]+=k;rest-=k;
        }
    }
    return flow-rest;
}
inline void dfs(int x)
{
    vis[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(!e[i])continue;
        if(vis[tn])continue;
        dfs(tn);
    }
}
inline void solve(int p)//处理第p位数字
{
    len=1;
    memset(lin,0,sizeof(lin));
    for(int i=1;i<=n;++i)
    {
        mark[i]=0;
        if(flag[i])
        {
            if(f[i]&(1<<p))add(S,i,INF,0);//源点为1
            else 
            {
                add(i,T,INF,0);//汇点为0
                mark[i]=1;
            }
        }
        else
        {
            add(S,i,1,0);
            add(i,T,1,0);
            mark[i]=2;
        }
    }
    for(int i=1;i<=m;++i)
    {
        int x=s[i].x;
        int y=s[i].y;
        if(mark[x]==2&&mark[y]==2){add(x,y,1,1);continue;}
        if(mark[x]==mark[y])continue;
        if(mark[x]>mark[y])swap(x,y);
        if(mark[x]==1&&mark[y]==2){add(y,x,1,0);continue;}
        add(x,y,1,0);
    }
    flow=maxflow=0;
    while(bfs())while((flow=dinic(S,INF)))maxflow+=flow;
    memset(vis,0,sizeof(vis));
    dfs(S);
    for(int i=1;i<=n;++i)
    {
        if(flag[i])continue;
        f[i]=f[i]|(vis[i]<<p);
    }
    return;
}
int main()
{
    freopen("1.in","r",stdin);
    G=read();
    while(G--)
    {
        memset(f,0,sizeof(f));
        memset(flag,0,sizeof(flag));
        n=read();m=read();
        S=n+1;T=S+1;
        for(int i=1;i<=m;++i)
        {
            int x,y;
            x=read();y=read();
            s[i]=(wy){x,y};
        }
        k=read();
        for(int i=1;i<=k;++i)
        {
            int x,z;
            x=read();z=read();
            flag[x]=1;f[x]=z;
        }
        for(int i=30;i>=0;--i)solve(i);
        for(int i=1;i<=n;++i)printf("%d
",f[i]);
    }
    return 0;
}
View Code

觉得非常自然...

 

原文地址:https://www.cnblogs.com/chdy/p/11392303.html