luogu P4076 [SDOI2016]墙上的句子

luogu

loj

题意看了我半天(逃 (应该是我语文太差了)

题意是要确定每一行和每一列的看单词的顺序,使得同时正着出现和反着出现在里面的单词数量最少,每行和每列的性质是这一行所有单词反过来的单词要么字典序大于等于原来的,要么小于等于原来的

首先回文单词一定会出现,可以直接加入答案,以后就不用管了.对于剩下的单词,如果不想让他贡献答案,那就要使的每次读这个单词都是一个样子,例如"ABC" "CBA",那么第一个正着读,第二个反着读答案最小.为了方便,我们只保留单词字典序更小的形式,同时把行列的读到单词字典序更小的方向作为正方向.然后现在问题变成给每一行每一列确定方向,使得所在行列同时有正方向和反方向的单词数量最小(就是给每个集合确定黑/白颜色,使得所在集合中有黑集合和白集合的元素个数最小)

这个可以使用最小割解决,具体是给每行每列建点,如果正方向可以用就从原点\(ps\)向这个点\(i\)连容量为\(1\)的边\((ps,i,1)\),否则连\((ps,i,+\infty)\),如果反方向可以用就从\(i\)向汇点\(pt\)连容量为\(1\)的边\((i,pt,1)\),否则连\((i,pt,+\infty)\),这样如果最后\(i\)在\(pt\)集合就是正方向,否则就是反方向.然后对于每个单词\(j\)建两个点\(a1_j,a2_j\),连边\((ps,a1_j,p),(a2_j,pt,p)(p\)为一个大于\(n+m\)的数\()\),然后对于单词\(j\)所在的行列\(i\),连边\((a1_j,i,+\infty),(i,a2_j,+\infty)\),这样如果一个单词所在行列同时割在\(ps\)或者\(pt\)集合中,就要多割去一个\(p\),不然要多割掉\(2p\)并且给答案加上\(2\)(一个非回文单词要统计两次).最后答案为回文单词个数+\(2(\lfloor\frac{flow}{p}\rfloor-\)非回文单词个数\()\)

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long

using namespace std;
const int N=75,M=N*N*2,inf=1<<29;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
    return x*w;
}
int to[M*4],nt[M*4],c[M*4],hd[M],tot=1;
void add(int x,int y,int z)
{
    ++tot,to[tot]=y,nt[tot]=hd[x],c[tot]=z,hd[x]=tot;
    ++tot,to[tot]=x,nt[tot]=hd[y],c[tot]=0,hd[y]=tot;
}
int ps,pt,nhd[M],lv[M];
queue<int> q;
bool bfs()
{
    for(int i=ps;i<=pt;++i) lv[i]=0;
    lv[ps]=1,q.push(ps);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=hd[x];i;i=nt[i])
        {
            int y=to[i];
            if(c[i]>0&&!lv[y])
            {
                lv[y]=lv[x]+1;
                q.push(y);
            }
        }
    }
    return lv[pt];
}
int dfs(int x,int fw)
{
    if(x==pt) return fw;
    int an=0;
    for(int &i=nhd[x];i;i=nt[i])
    {
        int y=to[i];
        if(c[i]>0&&lv[y]==lv[x]+1)
        {
            int dt=dfs(y,min(fw,c[i]));
            c[i]-=dt,c[i^1]+=dt;
            fw-=dt,an+=dt;
            if(!fw) break;
        }
    }
    return an;
}
int dinic()
{
    int an=0,dt=0;
    while(bfs())
    {
        for(int i=ps;i<=pt;++i) nhd[i]=hd[i];
        while((dt=dfs(ps,inf))) an+=dt;
    }
    return an;
}
map<string,int> mp;
vector<int> ls[N*N];
string ss[N*N];
char cc[N][N];
int n,m,ans,w1[N],w2[N],tt;

int main()
{
    int T=rd();
    while(T--)
    {
        memset(hd,0,sizeof(hd)),tot=1;
        n=rd(),m=rd();
        ps=0,pt=n+m+n*m+1;
        for(int i=1;i<=n;++i) w1[i]=rd();
        for(int i=1;i<=m;++i) w2[i]=rd();
        for(int i=1;i<=n;++i) scanf("%s",cc[i]+1);
        mp.clear(),tt=0;
        for(int i=1;i<=n;++i)
        {
            string nw,rnw;
            bool fg=0;
            for(int j=1;j<=m+1;++j)
            {
                if(cc[i][j]&&cc[i][j]!='_') nw.push_back(cc[i][j]);
                else if(!nw.empty())
                {
                    rnw=nw;
                    reverse(rnw.begin(),rnw.end());
                    if(!fg&&rnw!=nw)
                    {
                        fg=1;
                        if(rnw<nw) nw=rnw,w1[i]=-w1[i];
                    }
                    if(!mp.count(nw)) mp[nw]=++tt,ss[tt]=nw,ls[tt].clear();
                    ls[mp[nw]].push_back(i);
                    nw.clear();
                }
            }
        }
        for(int j=1;j<=m;++j) cc[n+1][j]=0;
        for(int j=1;j<=m;++j)
        {
            string nw,rnw;
            bool fg=0;
            for(int i=1;i<=n+1;++i)
            {
                if(cc[i][j]&&cc[i][j]!='_') nw.push_back(cc[i][j]);
                else if(!nw.empty())
                {
                    rnw=nw;
                    reverse(rnw.begin(),rnw.end());
                    if(!fg&&rnw!=nw)
                    {
                        fg=1;
                        if(rnw<nw) nw=rnw,w2[j]=-w2[j];
                    }
                    if(!mp.count(nw)) mp[nw]=++tt,ss[tt]=nw,ls[tt].clear();
                    ls[mp[nw]].push_back(j+n);
                    nw.clear();
                }
            }
        }
        for(int i=1;i<=n;++i)
        {
            add(ps,i,w1[i]>=0?1:inf);
            add(i,pt,w1[i]<=0?1:inf);
        }
        for(int j=1;j<=m;++j)
        {
            add(ps,j+n,w2[j]>=0?1:inf);
            add(j+n,pt,w2[j]<=0?1:inf);
        }
        int pc=n+m,dt=0;
        ans=0;
        for(int i=1;i<=tt;++i)
        {
            string rnw=ss[i];
            reverse(rnw.begin(),rnw.end());
            if(ss[i]==rnw){++ans;continue;}
            dt+=233;
            ++pc,++pc;
            add(ps,pc-1,233),add(pc,pt,233);
            sort(ls[i].begin(),ls[i].end());
            vector<int>::iterator it;
            int la=0;
            for(it=ls[i].begin();it!=ls[i].end();++it)
            {
                int x=*it;
                if(x==la) continue;
                add(pc-1,x,inf),add(x,pc,inf);
                la=x;
            }
        }
        ans+=((dinic()-dt)/233)*2;
        printf("%d\n",ans);
    }
    return 0;
}
上一篇:[SDOI2016]储能表


下一篇:[SDOI2016]数字配对