• 【题解】毒瘤 OI 刷题汇总 [SCOI2012]


    【题解】毒瘤 OI 刷题汇总 [SCOI2012]

    由于不清楚题目顺序,就按照 ( ext{BZOJ}) 上面的排列好了。


    【Day1 T1】滑雪

    传送门:滑雪 ( ext{[P2573]}) ( ext{[Bzoj2753]})

    【题目描述】

    给出一个由 (n) ((nleqslant 10^5)) 个点、(m) ((mleqslant 10^6)) 条边组成的无向图,点和边均有权值,求以 (1) 为根的有向树形图,对于每条选出来的有向边 ((x,y)) 必须满足 (x) 的权值大于等于 (y) 的权值,在包含点数最大的前提下,求出最大边权和。

    【分析】

    拓扑 + 最大生成树 乱搞。

    首先是求能包含的最大点数,其实质就是从 (1) 出发走合法有向边能到达的点的个数,拓扑排序暴力统计即可。

    在拓扑的过程中顺带把经过的边全部存起来,然后在这些边中选出一部分使得边权和最大,其实就是个最大生成树,(kruscal) 暴力搞搞就可以了。

    时间复杂度:(O(mlogm))

    【Code】

    #include<algorithm>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=1e5+3,M=1e6+3;
    int n,m,o,x,y,z,h,t,cnt,Q[N],A[N],fa[N],vis[N],head[N];LL ans;
    struct QAQ{int w,to,next;}a[M<<1];
    struct QWQ{int x,y,z;inline bool operator<(const QWQ &O)const{return A[y]!=A[O.y]?A[y]>A[O.y]:z<O.z;};}B[M<<1];
    inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline int find(Re x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    int main(){
    //    freopen("456.txt","r",stdin);
        in(n),in(m);
        for(Re i=1;i<=n;++i)in(A[i]);
        while(m--){
            in(x),in(y),in(z);
            if(A[x]>=A[y])add(x,y,z);//注意权值相等时要连两条边
            if(A[y]>=A[x])add(y,x,z);
        }
        m=0,h=1,t=0,Q[++t]=1,vis[1]=1;
        while(h<=t){
            Re x=Q[h++];++cnt;
            for(Re i=head[x],to;i;i=a[i].next)
                if(A[x]>=A[to=a[i].to]){
                    B[++m].x=x,B[m].y=to,B[m].z=a[i].w;
                    if(!vis[to])vis[to]=1,Q[++t]=to;
                }
        }
        sort(B+1,B+m+1);
        for(Re i=1;i<=n;++i)fa[i]=i;
        for(Re i=1,t=0;i<=m&&t<cnt-1;++i)
            if((x=find(B[i].x))!=(y=find(B[i].y)))
                ans+=B[i].z,fa[x]=y,++t;
        printf("%d %lld
    ",cnt,ans);
    }
    

    【Day1 T2】喵星球上的点名

    传送门:喵星球上的点名 ( ext{[P2336]}) ( ext{[Bzoj2754]})

    【题目描述】

    给定 (n) ((nleqslant 5*10^4)) 对字符串 (a_i,b_i) 表示喵咪的信息,以及 (m) 个询问串 (s_j) 。在一次询问中,若 (s_j)(a_i) 或者 (b_i) 的子串,则 (i) 会被统计一次。

    对于每次询问输出统计到的喵咪个数,最后再对于每个喵咪,输出在 (m) 次询问中被统计的次数。

    【分析】

    字符串经典题,大部分高级字符串匹配算法都可以艹过。

    由于我 菜+懒,只写了广义后缀自动机的做法。

    其实用广义 ( ext{SAM}) 的话这就是板子题,统计答案时暴力跳 (parent) 并染色,遇到已经染过的地方就跳过。

    然后...就完了...

    时间复杂度:(O(n* ext{玄学}))

    听巨佬说暴跳 (parent) 可以被卡到根号,但如果用 (dfs) 序优化的话,能做到上界 (O(nlog n))(不管这么多了,反正能过就行)。

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #include<map>
    #define Re register int
    #define LL long long
    using namespace std;
    const int N=2e5+5;
    int n,t,T,x,s[N],ch[N],Len[N];
    inline void in(Re &x){
        int fu=0;x=0;char c=getchar();
        while(c<'0'||c>'9')fu|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=fu?-x:x;
    }
    struct Suffix_Automaton{    
        int O,pos[N],cnt[N],link[N],maxlen[N],minlen[N];queue<int>Q;map<int,int>trans[N];
        Suffix_Automaton(){O=1;}
        inline int insert(Re ch,Re last){
            if(trans[last][ch]&&maxlen[last]+1==maxlen[trans[last][ch]])return trans[last][ch];
            Re x,y,z=++O,p=last,flag=0;maxlen[z]=maxlen[last]+1;
            while(p&&!trans[p][ch])trans[p][ch]=z,p=link[p];
            if(!p)link[z]=1;
            else{
                x=trans[p][ch];
                if(maxlen[p]+1==maxlen[x])link[z]=x;
                else{
                    if(maxlen[p]+1==maxlen[z])flag=1;
                    y=++O;maxlen[y]=maxlen[p]+1;
                    trans[y]=trans[x];
                    while(p&&trans[p][ch]==x)trans[p][ch]=y,p=link[p];
                    link[y]=link[x],link[z]=link[x]=y;
                }
            }
            return flag?y:z;
        }
        int co[N];
        inline void updata(Re p,Re id){
            while(p&&co[p]!=id)++cnt[p],co[p]=id,p=link[p];
        }
        int gs[N],Ans[N];
        inline void updata_(Re p,Re id){
            while(p&&co[p]!=id)Ans[id]+=gs[p],co[p]=id,p=link[p];
        }
        inline void ask(Re ch[],Re L){
            Re p=1,flag=1;
            for(Re i=1;i<=L&&flag;++i){
                Re a=ch[i];
                if(trans[p][a])p=trans[p][a];
                else flag=0;
            }
            if(flag)++gs[p];
            printf("%d
    ",flag?cnt[p]:0);
        }
    }SAM;
    int main(){
    //    freopen("123.txt","r",stdin);
        in(n),in(T);
        for(Re i=1;i<=n;++i){
            in(Len[(i<<1)-1]);Re last=1;
            for(Re j=1;j<=Len[(i<<1)-1];++j)in(x),last=SAM.insert(s[++t]=x,last);
            in(Len[i<<1]);last=1;
            for(Re j=1;j<=Len[i<<1];++j)in(x),last=SAM.insert(s[++t]=x,last);
        }
        for(Re i=1,t=0;i<=n;++i){
            for(Re j=1,p=1;j<=Len[(i<<1)-1];++j)
                SAM.updata(p=SAM.trans[p][s[++t]],i);
            for(Re j=1,p=1;j<=Len[i<<1];++j)
                SAM.updata(p=SAM.trans[p][s[++t]],i);
        }
        while(T--){
            in(Len[0]);
            for(Re i=1;i<=Len[0];++i)in(ch[i]);
            SAM.ask(ch,Len[0]);
        }
        memset(SAM.co,0,sizeof(SAM.co));
        for(Re i=1,t=0;i<=n;++i){
            for(Re j=1,p=1;j<=Len[(i<<1)-1];++j)
                SAM.updata_(p=SAM.trans[p][s[++t]],i);
            for(Re j=1,p=1;j<=Len[i<<1];++j)
                SAM.updata_(p=SAM.trans[p][s[++t]],i);
        }
        for(Re i=1;i<=n;++i)printf("%d ",SAM.Ans[i]);
    }
    

    【Day1 T3】喵星人入侵

    传送门:喵星人入侵 ( ext{[P2337]}) ( ext{[Bzoj2755]})

    【题目描述】

    略。

    【分析】

    又是毒瘤插头 (dp),不会,先咕着。

    【Code】

    不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)
    

    【Day2 T1】奇怪的游戏

    传送门:奇怪的游戏 ( ext{[P5038]}) ( ext{[Bzoj2756]})

    【题目描述】

    共有 (T) ((Tleqslant 10)) 组数据,每组数据给出一个 (n imes m) ((n,mleqslant 40)) 的棋盘,每个位置 ((i,j)) 上的数为 (v[i][j])

    一次操作可以选择两个相邻的数,并使这数都加上 (1),求最少需要多少次操作才能使棋盘中的所有数都相同,如果无解输出 (-1)

    【分析】

    二分 + 最大流判断可行性。

    注意到每次操作都是选择两个相邻的位置,可以先对棋盘黑白染色,建出二分图。

    设黑点、白点分别有 (A,B) 个,其权值总和分别为 (a,b),设操作次数为 (k),最后所有数字都变成了 (X),由于每次操作都会使黑点总权值增加 (1),则有:(a+k=A*X),同理得 (b+k=B*X),联立得 (k=A*X-a=B*X-b),即 ((A-B)*X=a-b)

    (A eq B) 时,可以直接算出 (X),但这个 (X) 可能并不合法,所以还要跑最大流 (judge) 一下。

    (A=B) 时,此时如果 (a eq b) 则说明一定无解(每次操作并不会改变 (a,b) 的相对大小,(a,b) 永远都不能相等)。
    由于黑白点个数相等,可知 (n*m) 为偶数,如果某一个 (X) 是可行的,那么进行 (frac{n*m}{2}) 次操作可以将所有数都变为 (X+1),即说明 (X+1) 也一定是可行的。所以这个 (X) 是可以二分,每次跑最大流判断可行性即可。

    关于如何跑最大流 (judge)

    在已经确定目标值 (X) 的情况下,每个位置 ((i,j)) 上的点需要被操作 (X-v[i][j]) 次。

    分别设立超源、超汇,超源向所有白点连容量为 (X-v[i][j]) 的边,所有黑点向超汇连容量为 (X-v[i][j]) 的边,所有白点到相邻的黑点连容量为 (inf) 的边,跑一遍 (Dinic) 算法,如果最大流 (maxflow=sum_{(i,j)in{ ext{白点}}}(X-v[i][j])),则说明 (X) 是可行的。

    时间复杂度:(O(log (inf) nmsqrt{nm})),带一个取决于建边数量的常数。

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=1600+5,M=N*3+5;//点数:N=nm 边数:M=N/2*4+N/2*2=3N
    const LL inf=1e18;
    int n,m,T,st,ed;
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    struct Dinic{
        int o,h,t,Q[N],cur[N],dis[N],head[N];LL maxflow;
        struct QAQ{int to,next;LL flow;}a[M<<1];
        inline void add_(Re x,Re y,LL flow){a[++o].to=y,a[o].flow=flow,a[o].next=head[x],head[x]=o;}
        inline void add(Re x,Re y,LL flow){add_(x,y,flow),add_(y,x,0);}
        inline void CL(){memset(head,0,sizeof(head)),o=1.,maxflow=0;}
        inline int bfs(Re st,Re ed){
            for(Re i=0;i<=ed;++i)dis[i]=0,cur[i]=head[i];
            h=1,t=0,dis[Q[++t]=st]=1;
            while(h<=t){
                Re x=Q[h++];
                for(Re i=head[x],to;i;i=a[i].next)
                    if(a[i].flow&&!dis[to=a[i].to]){
                        dis[to]=dis[x]+1,Q[++t]=to;
                        if(to==ed)return 1;
                    }
            }
            return 0;
        }
        inline LL dfs(Re x,LL flow){
            if(x==ed||!flow)return flow;
            LL tmp=0,f;
            for(Re i=cur[x],to;i;i=a[i].next){
                cur[x]=i;
                if(dis[to=a[i].to]==dis[x]+1&&(f=dfs(to,min(a[i].flow,flow-tmp)))){
                    a[i].flow-=f,a[i^1].flow+=f,tmp+=f;
                    if(tmp==flow)break;
                }
            }
            return tmp;
        }
        inline void dinic(Re st,Re ed){while(bfs(st,ed))maxflow+=dfs(st,inf);}
    }T1;
    int cnt1,cnt2,MaxA,A[43][43],wx[4]={0,0,1,-1},wy[4]={1,-1,0,0};LL sp1,sp2;
    inline int Poi(Re i,Re j){return (i-1)*m+j;}
    inline int judge(LL X){
        LL S=0;T1.CL();
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=m;++j)
                if((i+j)&1)T1.add(Poi(i,j),ed,X-A[i][j]);
                else{
                    T1.add(st,Poi(i,j),X-A[i][j]),S+=X-A[i][j];
                    for(Re k=0;k<4;++k){
                        Re x=i+wx[k],y=j+wy[k];
                        if(x>=1&&x<=n&&y>=1&&y<=m)T1.add(Poi(i,j),Poi(x,y),inf);
                    }
                }
        T1.dinic(st,ed);
        return T1.maxflow==S;
    }
    int main(){
    //    freopen("123.txt","r",stdin);
        in(T);
        while(T--){
            in(n),in(m),st=n*m+1,ed=st+1,MaxA=cnt1=cnt2=sp1=sp2=0;
            for(Re i=1;i<=n;++i)
                for(Re j=1;j<=m;++j){
                    in(A[i][j]),MaxA=max(MaxA,A[i][j]);
                    if((i+j)&1)sp1+=A[i][j],++cnt1;
                    else sp2+=A[i][j],++cnt2;
                }
            if(cnt1!=cnt2){
                LL X=(sp1-sp2)/(cnt1-cnt2);
                if(X>=MaxA&&judge(X))printf("%lld
    ",X*cnt1-sp1);
                else puts("-1");
            }
            else{
                if(sp1!=sp2){puts("-1");continue;}
                LL r=inf,l=MaxA;
                while(l<r){
                    LL mid=l+r>>1;
                    LL tmp=l+r>>1;
                    if(judge(mid))r=mid;
                    else l=mid+1;
                }
                printf("%lld
    ",r==inf?-1:r*cnt1-sp1);
            }
        }
    }
    

    【Day2 T2】Blinker 的仰慕者

    传送门:( ext{Blinker}) 的仰慕者 ( ext{[P5842]}) ( ext{[Bzoj2757]})

    【题目描述】

    【分析】

    又是毒瘤数位 (dp),不会,先咕着。

    【Code】

    不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)
    

    【Day2 T3】Blinker 的噩梦

    传送门:( ext{Blinker}) 的噩梦 ( ext{[P5843]}) ( ext{[Bzoj2758]})

    【题目描述】

    略。

    【分析】

    毒瘤 (OI) 的毒瘤计算几何终于开始显示其毒瘤本质了。

    自己随便瞎造的数据 把网上仅有的几篇题解都给卡了,先咕着吧。。。

    【Code】

    不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)
    

  • 相关阅读:
    java ArrayList存储基本类型
    java ArrayList的几种方法使用
    java ArrayList的基本使用
    java 猜数字
    java Random随机生成一个数
    java Scanner输入数字、字符串
    java 构造方法
    java this的应用
    java pravite关键字的使用
    云计算服务
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12710745.html
Copyright © 2020-2023  润新知