省选武汉联测 3

T2 打错文件爆了,掉大分。菜的真实。

春季赛出分了,被薄纱。菜的真实。

回文串(大回文)

签到题。首先把左右相同的去掉,就得到了可能的左端点和右端点。定住一个端点暴力枚举另一个,哈希值拼接一下判断回文就行了。注意两边都要做一遍。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int mod=1000000007,prm=131;
int n,hs[100010],hs2[100010],p[100010];
char s[100010];
int geths(int l,int r){
    return (hs[r]-1ll*hs[l-1]*p[r-l+1]%mod+mod)%mod;
}
int geths2(int l,int r){
    return (hs2[l]-1ll*hs2[r+1]*p[r-l+1]%mod+mod)%mod;
}
int merge(int l1,int r1,int l2,int r2){
    int h1=geths2(l1,r1),h2=geths(l2,r2);
    return (1ll*h1*p[r2-l2+1]+h2)%mod;
}
int merge2(int l1,int r1,int l2,int r2){
    int h1=geths(l1,r1),h2=geths2(l2,r2);
    return (1ll*h1*p[r2-l2+1]+h2)%mod;
}
void solve(int l,int r){
    int mid=(r-l+1)>>1;
    for(int i=l;i<r;i++){
        if(i<l+mid-1){
            if(merge(l,i,i+1,l+mid-1)==geths2(r-mid+1,r)){
                printf("%d %d\n",l,i);return;
            }
        }
        else if(i==l+mid-1){
            if(geths(l,i)==geths(r-mid+1,r)){
                printf("%d %d\n",l,i);return;
            }
        }
        else{
            if(geths(i-mid+1,i)==merge(l,l+mid-r+i-1,i+1,r)){
                printf("%d %d\n",l,i);return;
            }
        }
    }
    for(int i=r;i>l;i--){
        if(i>r-mid+1){
            if(geths2(l,l+mid-1)==merge2(r-mid+1,i-1,i,r)){
                printf("%d %d\n",i,r);return;
            }
        }
        else if(i==r-mid+1){
            if(geths(l,l+mid-1)==geths(i,r)){
                printf("%d %d\n",i,r);return;
            }
        }
        else{
            if(merge2(l,i-1,r-mid-l+i+1,r)==geths(i,i+mid-1)){
                printf("%d %d\n",i,r);return;
            }
        }
    }
    puts("-1 -1");
}
int main(){
    p[0]=1;
    for(int i=1;i<=100000;i++)p[i]=1ll*p[i-1]*prm%mod;
    int tim;scanf("%d",&tim);
    while(tim--){
        scanf("%d%s",&n,s+1);hs[0]=hs2[n+1]=0;
        for(int i=1;i<=n;i++)hs[i]=(1ll*hs[i-1]*prm+s[i])%mod;
        for(int i=n;i>=1;i--)hs2[i]=(1ll*hs2[i+1]*prm+s[i])%mod;
        int l=1,r=n;
        while(l<r&&s[l]==s[r])l++,r--;
        if(l>=r){
            puts("1 1");continue;
        }
        solve(l,r);
    }
}

国际象棋(大马)

打表题。发现 \(n,m\ge 3\) 时答案可以取到最大。把矩阵行列按照 \(4\times 4\) 分组后内部构造,剩下的散块行列都是 \(3-6\),打表。

表是贺的。拜谢。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
int n,m;
bool jud;
void print(int a,int b,int c,int d){
    if(jud)printf("%d %d %d %d\n",b,a,d,c);
    else printf("%d %d %d %d\n",a,b,c,d);
}
int main(){
    int tim;scanf("%d",&tim);
    while(tim--){
        jud=false;
        scanf("%d%d",&n,&m);
        if(n==1||m==1){
            puts("0");continue;
        }
        if(n==2||m==2){
            if(m==2)swap(n,m),jud=true;
            int ans=(m>>2)<<2;
            if(m%4==3)ans+=2;
            printf("%d\n",ans);
            for(int i=1;i<=m;i++){
                if(i%4==0||i%4==3){
                    print(1,i-2,2,i);print(2,i-2,1,i);
                }
            }
            continue;
        }
        printf("%d\n",n*m>>1);
        while(n!=0&&n!=3&&n!=5&&n!=6){
            for(int j=3;j<=m;j++){
                print(n-3,j-2,n-2,j);print(n-1,j-2,n,j);
            }
            print(n-2,1,n,2);print(n-2,2,n,1);
            print(n-3,m-1,n-1,m);print(n-3,m,n-1,m-1);
            n-=4;
        }
        if(!n)continue;
        while(m!=0&&m!=3&&m!=5&&m!=6){
            for(int j=3;j<=n;j++)print(j-2,m-3,j,m-2),print(j-2,m-1,j,m);
            print(1,m-2,2,m),print(2,m-2,1,m);
            print(n-1,m-3,n,m-1),print(n,m-3,n-1,m-1);
            m-=4;
        }
        if(!m)continue;
        if(n>m)swap(n,m),jud=true;
        if(n==3){
            if(m==3){
                print(1,1,2,3);
                print(2,1,3,3);
                print(1,2,3,1);
                print(1,3,3,2);
            }
            if(m==5){
                print(1,1,3,2);
                print(1,2,2,4);
                print(1,3,2,1);
                print(1,4,3,5);
                print(2,2,3,4);
                print(2,3,3,1);
                print(2,5,3,3);
            }
            if(m==6){
                print(1,1,3,2);
                print(2,1,3,3);
                print(3,1,2,3);
                print(3,4,2,6);
                print(3,5,1,6);
                print(3,6,1,5);
                print(1,2,2,4);
                print(1,4,2,2);
                print(1,3,2,5);
            }
            
        }
        if(n==5){
            if(m==5){
                print(1,1,2,3);
                print(1,2,3,1);
                print(1,3,2,5);
                print(1,4,2,2);
                print(1,5,3,4);
                print(2,1,4,2);
                print(2,4,4,5);
                print(3,2,5,1);
                print(3,5,5,4);
                print(4,1,5,3);
                print(4,3,5,5);
                print(4,4,5,2);
            }
            if(m==6){
                print(1,1,3,2);
                print(1,2,3,3);
                print(1,3,2,1);
                print(1,4,2,6);
                print(1,5,2,3);
                print(1,6,2,4);
                print(2,2,4,1);
                print(2,5,4,4);
                print(3,1,5,2);
                print(3,4,4,6);
                print(3,5,5,6);
                print(3,6,5,5);
                print(4,2,5,4);
                print(4,3,5,1);
                print(4,5,5,3);
            }
        }
        if(n==6){
            if(m==6){
                print(1,1,2,3);
                print(1,2,3,3);
                print(1,3,2,1);
                print(1,4,2,2);
                print(1,5,3,6);
                print(1,6,3,5);
                print(2,4,3,2);
                print(2,5,4,6);
                print(2,6,3,4);
                print(3,1,5,2);
                print(4,1,6,2);
                print(4,2,6,1);
                print(4,3,5,1);
                print(4,4,6,5);
                print(4,5,5,3);
                print(5,4,6,6);
                print(5,5,6,3);
                print(5,6,6,4);
            }
        }
    }
    return 0;
}

嘉心糖(大枝江)

首先拜谢 u 群大佬,我们知道了这道题是 Gym 103428 L。

然后:然然……嘿嘿我的然然……(指陈嘉然先生)

然后是正解。首先这玩意让我们求图的最大团,数据范围还贼大。显然不能爆搜。

考虑到最大团等于补图最大独立集,算这个东西。

然后观察操作的性质:每次操作都会交换。这样如果三个人 \(i<j<k\)\(i,j\) 没有连边,\(j,k\) 没有连边,那么 \(i,k\) 一定不可能连边。于是把补图强制定序,规定 \(i<j\) 则从 \(i\) 连到 \(j\) 一条边,发现补图是个闭包。于是最大独立集就是最长反链长度(反链是啥?一条链,每两个点都不能互相到达)。证明是容易的,考虑反证。如果有路径相通那一定直接相连。

根据 Dilworth 定理,最长反链长度 = 最小链覆盖。最小链覆盖是容易求的,拆点二分图跑最大匹配。然而这样边数爆炸。

我们知道网络流是个黑盒(确信),于是网络流似了。使用比较不黑盒的匈牙利算法。

先看如何优化普通的匈牙利:由于每次只会搜到每个节点一次,所以如果能够快速知道哪些出边合法就可以快速增广。这个可以用并查集:每次匹配一条增广路的时候把要匹配的节点的父亲设为下一个节点,这样一次路径压缩就可以快速跳过不合法的点。单次增广是 \(O(n\alpha(n)+m)\) 的。

\(n\)\(200000\),考虑减少增广次数。把所有点按照度数排序,从小到大扫,如果所有出边能直接匹配就匹配。可以证明这样最后会剩下 \(O(\sqrt m)\) 个点。后边都能匹配上。暴力匈牙利即可。

算了补一下详细证明。假设第一步匹配 \(k\) 个点,那么剩下的点显然度数小于 \(k\)。于是原来的所有点度数都 \(\ge n-k\)。而边数 \((n-k)^2\le m\),因此是 \(O(\sqrt m)\) 级别的。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
using namespace std;
int n,m,ans,d[200010],a[200010],p[200010],fa[200010],matchl[200010],matchr[200010];
set<int,greater<int> >s;
set<int>g[200010];
int find(int x){
    return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
}
bool cmp(int a,int b){
    return d[a]<d[b];
}
bool dfs(int x){
    for(int v=find(x+1);v<=n;v=find(v+1)){
        if(g[x].find(v)!=g[x].end())continue;
        fa[v]=v+1;
        if(matchr[v]==0||dfs(matchr[v])){
            matchl[x]=v;matchr[v]=x;
            return true;
        }
    }
    return false;
}
int main(){
    freopen("sugar.in","r",stdin);
    freopen("sugar.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        d[i]=n-i;p[i]=a[i]=i;s.insert(i);
    }
    for(int i=1;i<=m;i++){
        int x;scanf("%d",&x);
        g[min(a[x],a[x+1])].insert(max(a[x],a[x+1]));
        d[min(a[x],a[x+1])]--;swap(a[x],a[x+1]);
    }
    sort(p+1,p+n+1,cmp);
    for(int i=1;i<=n;i++){
        if(d[p[i]]>ans){
            for(int x:s){
                if(x>p[i]&&g[p[i]].find(x)==g[p[i]].end()){
                    matchl[p[i]]=x;matchr[x]=p[i];
                    s.erase(x);ans++;break;
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(!matchl[i]){
            for(int j=1;j<=n+1;j++)fa[j]=j;
            if(dfs(i))ans++;
        }
    }
    printf("%d\n",n-ans);
    return 0;
}
posted @ 2023-03-10 16:49  gtm1514  阅读(17)  评论(0编辑  收藏  举报