[luogu8291]学术社区
对所有消息建图,其中\((x,y)\)的边权为当\(x\)的下一条消息为\(y\)时的收益
具体的,图中包含以下两类边(边权为\(1\)):
- 对于楼上消息,假设其提到的网友为\(s\),其向\(s\)发出的消息连边
- 对于楼下消息,假设其提到的网友为\(s\),\(s\)发出的消息向其连边
另外,特殊性质\(C\)中的情况会产生重边,此时该边边权为\(2\)
此时,问题即选择边的子集,满足每个点出入度均不超过\(1\),并最大化边权和
结论1:对于重边\((x,y)\),存在一种最优方案选择该边
任取一组最优解,若未选择\((x,y)\),则分类讨论:
- 若其中\(x\)的出边存在且边权为\(2\),则交换\(y\)和该出边端点(两者完全相同)
- 类似的,若\(y\)的入边存在且边权为\(2\),则交换\(x\)和该入边端点
- 否则,直接删除\(x\)的出边和\(y\)的入边,并加入\((x,y)\),显然不劣
根据此结论,可以将图中\(x\)的出边和\(y\)的入边均删除,并将最终答案\(+2\)
重复此过程,最终图中所有边的边权均为\(1\),问题也即求最小路径覆盖
结论2:将该图视作DAG求最小路径覆盖,答案不变
对于通常的有向图,该做法问题在于可能选择一个简单环
注意到简单环中总包含非学术消息(学术消息间无边),不妨假设包含楼上消息
楼上消息的入边(另一个端点)必然也为楼上消息,以此类推整个环均为楼上消息
此时,任取图中某个消息,在该处断开并插入到对应的学术消息前即可
重复上述过程,最终可得一组不包含简单环的方案,即得证
根据此结论,并简单优化建边,由于二分图至多增广\(\sqrt{m}\)次,时间复杂度为\(O(m\sqrt{m})\)
建边时使用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;
}