[luogu8291]学术社区

对所有消息建图,其中(x,y)的边权为当x的下一条消息为y时的收益

具体的,图中包含以下两类边(边权为1):

  • 对于楼上消息,假设其提到的网友为s,其向s发出的消息连边
  • 对于楼下消息,假设其提到的网友为ss发出的消息向其连边

另外,特殊性质C中的情况会产生重边,此时该边边权为2

此时,问题即选择边的子集,满足每个点出入度均不超过1,并最大化边权和

结论1:对于重边(x,y),存在一种最优方案选择该边

任取一组最优解,若未选择(x,y),则分类讨论:

  • 若其中x的出边存在且边权为2,则交换y和该出边端点(两者完全相同)
  • 类似的,若y的入边存在且边权为2,则交换x和该入边端点
  • 否则,直接删除x的出边和y的入边,并加入(x,y),显然不劣

根据此结论,可以将图中x的出边和y的入边均删除,并将最终答案+2

重复此过程,最终图中所有边的边权均为1,问题也即求最小路径覆盖

结论2:将该图视作DAG求最小路径覆盖,答案不变

对于通常的有向图,该做法问题在于可能选择一个简单环

注意到简单环中总包含非学术消息(学术消息间无边),不妨假设包含楼上消息

楼上消息的入边(另一个端点)必然也为楼上消息,以此类推整个环均为楼上消息

此时,任取图中某个消息,在该处断开并插入到对应的学术消息前即可

重复上述过程,最终可得一组不包含简单环的方案,即得证

根据此结论,并简单优化建边,由于二分图至多增广m次,时间复杂度为O(mm)

建边时使用vector会比邻接表快很多,可能是出边顺序的问题

#include<bits/stdc++.h>
using namespace std;
#define N 80000
#define M 1000000
#define ull unsigned long long
int t,n,m,a[N],b[N],c[N];ull s1,s2,s3,id[N];
int V,E,it[M],d[M];queue<int>q;struct Data{int to,len,rev;};vector<Data>e[M];
int ans,pos[N],vis[N],pre[N],nex[N];vector<int>vl,vr;map<int,vector<int> >mat1[N],mat2[N];
int change(char c){
    if ((c>='A')&&(c<='Z'))return c-'A'+1;
    if ((c>='a')&&(c<='z'))return c-'a'+27;
    if (c=='_')return 53;
    if (c=='?')return 54;
    if (c=='!')return 55;
    return (c=='.' ? 56 : 0);
}
ull read(){
    ull x=0;int c=change(getchar());
    while (!c)c=change(getchar());
    while (c)x=x*57+c,c=change(getchar());
    return x;
}
void add(int x,int y,int z){
    e[x].push_back(Data{y,z,(int)e[y].size()});
    e[y].push_back(Data{x,0,(int)e[x].size()-1});
}
bool bfs(){
    for(int i=1;i<=V;i++)d[i]=-1;
    q.push(0);
    while (!q.empty()){
        int k=q.front();q.pop();
        for(Data i:e[k])
            if ((i.len)&&(d[i.to]<0))d[i.to]=d[k]+1,q.push(i.to);
    }
    return d[V]>=0;
}
int dfs(int k,int s){
    if (k==V)return s;
    int ans=0;
    for(int &i=it[k];i<e[k].size();i++)
        if ((e[k][i].len)&&(d[e[k][i].to]==d[k]+1)){
            int p=dfs(e[k][i].to,min(s,e[k][i].len));
            e[k][i].len-=p,e[e[k][i].to][e[k][i].rev].len+=p,s-=p,ans+=p;
            if (!s)return ans;
        }
    return ans;
}
int dinic(){
    int ans=0;
    while (bfs()){
        ans+=dfs(0,0x3f3f3f3f);
        for(int i=0;i<=V;i++)it[i]=0;
    }
    return ans;
}
void link(int x,int y){
    if (nex[x])pre[nex[x]]=0;
    if (pre[y])nex[pre[y]]=0;
    nex[x]=(x ? y : 0),pre[y]=(y ? x : 0);
}
int main(){
    scanf("%d",&t);
    while (t--){
        scanf("%d%d",&n,&m);
        V=(m+n<<1)+1,E=ans=0;
        for(int i=0;i<=V;i++)it[i]=0,e[i].clear();
        for(int i=1;i<=m;i++)vis[i]=pre[i]=nex[i]=0;
        for(int i=1;i<=n;i++)mat1[i].clear(),mat2[i].clear();
        for(int i=1;i<=n;i++)id[i]=read();
        sort(id+1,id+n+1);
        for(int i=1;i<=m;i++){
            s1=read(),s2=read(),s3=read();
            a[i]=lower_bound(id+1,id+n+1,s1)-id;
            b[i]=lower_bound(id+1,id+n+1,s2)-id;
            if ((b[i]>n)||(id[b[i]]!=s2))b[i]=c[i]=0;
            else c[i]=(s3==23305962750LL ? 2 : (s3==75721020011865LL));
            if (!c[i])pos[a[i]]=i;
            if (c[i]==1){
                if (mat2[b[i]][a[i]].empty())mat1[a[i]][b[i]].push_back(i);
                else{
                    int j=mat2[b[i]][a[i]].back();
                    mat2[b[i]][a[i]].pop_back();
                    ans+=2,link(i,j),vis[i]=vis[j]=1;
                }
            }
            if (c[i]==2){
                if (mat1[b[i]][a[i]].empty())mat2[a[i]][b[i]].push_back(i);
                else{
                    int j=mat1[b[i]][a[i]].back();
                    mat1[b[i]][a[i]].pop_back();
                    ans+=2,link(j,i),vis[i]=vis[j]=1;
                }
            }
        }
        for(int i=1;i<=m;i++){
            add(0,i,1),add(i+m,V,1);
            if ((!vis[i])||(c[i]!=1))add(i,a[i]+(m<<1),1);
            if ((!vis[i])||(c[i]!=2))add(a[i]+(m<<1)+n,i+m,1);
            if ((!vis[i])&&(c[i]==1))add(i,b[i]+(m<<1)+n,1);
            if ((!vis[i])&&(c[i]==2))add(b[i]+(m<<1),i+m,1);
        }
        ans+=dinic(),printf("%d\n",ans);
        for(int i=1;i<=n;i++){
            for(Data j:e[i+(m<<1)+n])e[i+(m<<1)].push_back(j);
            for(Data j:e[i+(m<<1)]){
                if ((j.to<=m)&&(j.len)){
                    if (vr.empty())vl.push_back(j.to);
                    else link(j.to,vr.back()),vr.pop_back();
                }
                if ((j.to>m)&&(!j.len)){
                    if (vl.empty())vr.push_back(j.to-m);
                    else link(vl.back(),j.to-m),vl.pop_back();
                }
            }
        }
        for(int i=1;i<=m;i++)vis[i]=0;
        for(int i=1;i<=m;i++)
            if (!vis[i]){
                int j=i;
                while (!vis[j])vis[j]=1,j=nex[j];
                if (j==i){
                    if (c[i]==1)j=pre[i],link(pre[pos[a[i]]],i),link(j,pos[a[i]]);
                    else j=nex[i],link(i,nex[pos[a[i]]]),link(pos[a[i]],j);
                }
            }
        for(int i=1;i<=m;i++)
            if (!pre[i]){
                for(int j=i;j;j=nex[j])printf("%d ",j);
            }
        printf("\n");
    }
    return 0;
}
posted @   PYWBKTDA  阅读(100)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-05-19 [loj2572]字符串
2021-05-19 [loj3103]节日庆典
点击右上角即可分享
微信分享提示