zoukankan      html  css  js  c++  java
  • 【题解】毒瘤 OI 刷题汇总 [SCOI2013]

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

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


    【Day1 T1】摩托车交易

    传送门:摩托车交易 ( ext{[P3280]}) ( ext{[Bzoj3322]})

    【题目描述】

    给出一个 (n) ((nleqslant 10^5)) 个点 (m) ((mleqslant 2*10^5)) 条边的无向连通图,图中的点、边带有边权,其中有 (q) ((0leqslant qleqslant n)) 为特殊点,对于任意两个特殊点之间都有一条权值为 (inf) 的边。

    现需要按照给定顺序依次经过这 (n) 个点,在某些点可以购买本子,某些点可以卖出本子,在各点的本子交易量不能超过该点的点权,经过某条边时所携带的本子数量不能超过该边的边权。

    在进行每次卖出交易时您想要使得本次的卖出量最大,并且在与最后一个点进行交易后手上不能有剩余的本子。请按照顺序依次输出最大卖出量。

    【分析】

    最大生成树 + 倍增 (LCA) 求路径最小值,模拟本子交易。

    注意到我们只需要尽量携带最多的本子,不必在意是否卖光的问题,因为实际行动时可以通过调整数量来避免多余本子的出现。

    很显然我们可以先求出这 (n) 个点的最大生成树,每次移动时走树上的路径就可以保证本子运送量最大。

    处理特殊点显然不能暴力枚举,可以新建一个中转点 (n+1),所有特殊点均向 (n+1) 连一条权值为 (inf) 的边即可(注意细节:新建节点后求生成树就是 (n+1) 个节点了,还有就是当不存在特殊点时不能新建中转点,否则会不连通)。

    然后随便写个倍增 (LCA) 或者树剖求树上路径最小值,设个变量 (v) 表示目前携带的本子数量,瞎模拟一下就可以了。

    时间复杂度:(O((m+q)log(m+q)+nlog n))

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register LL
    using namespace std;
    const LL N=1e5+3,M=2e5+3,inf=1e18,logN=17;
    LL n,m,x,y,o,T,A[N],ip[N],fa[N],head[N];
    struct QAQ{LL w,to,next;}a[N<<1];
    inline void add(Re x,Re y,Re z){a[++o].to=y,a[o].w=z,a[o].next=head[x],head[x]=o;}
    struct QWQ{LL x,y,z;inline bool operator<(const QWQ &O)const{return z>O.z;}}b[M+N];
    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 LL find(Re x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    struct LCA{
        LL deep[N],dis[N][20],ant[N][20];
        inline void dfs(Re x,Re fa,Re w){
            deep[x]=deep[ant[x][0]=fa]+1,dis[x][0]=w;
            for(Re i=1;(1<<i)<=deep[x];++i)ant[x][i]=ant[ant[x][i-1]][i-1],dis[x][i]=min(dis[x][i-1],dis[ant[x][i-1]][i-1]);
            for(Re i=head[x];i;i=a[i].next)if(a[i].to!=fa)dfs(a[i].to,x,a[i].w);
        }
        inline LL ask(Re x,Re y){
            if(deep[x]<deep[y])swap(x,y);Re ans=inf;
            for(Re i=logN;i>=0;--i)if(deep[ant[x][i]]>=deep[y])ans=min(ans,dis[x][i]),x=ant[x][i];
            if(x==y)return ans;
            for(Re i=logN;i>=0;--i)if(ant[x][i]!=ant[y][i])ans=min(ans,min(dis[x][i],dis[y][i])),x=ant[x][i],y=ant[y][i];
            return min(ans,min(dis[x][0],dis[y][0]));
        }
    }T1;
    int main(){
    //    freopen("123.txt","r",stdin);
        in(n),in(m),in(T);
        for(Re i=1;i<=n;++i)in(ip[i]);
        for(Re i=1;i<=n;++i)in(A[i]);
        for(Re i=1;i<=m;++i)in(b[i].x),in(b[i].y),in(b[i].z);
        if(T){
            while(T--)in(x),b[++m].x=x,b[m].y=n+1,b[m].z=inf;
            sort(b+1,b+m+1);
            for(Re i=1;i<=n+1;++i)fa[i]=i;
            for(Re i=1,t=0;i<=m&&t<n;++i)
                if((x=find(b[i].x))!=(y=find(b[i].y)))
                    ++t,fa[x]=y,add(b[i].x,b[i].y,b[i].z),add(b[i].y,b[i].x,b[i].z);
        }
        else{
            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<n-1;++i)
                if((x=find(b[i].x))!=(y=find(b[i].y)))
                    ++t,fa[x]=y,add(b[i].x,b[i].y,b[i].z),add(b[i].y,b[i].x,b[i].z);
        }
        T1.dfs(1,0,inf);LL w=1,v=A[ip[1]];
        while(v<0)puts("0"),v=A[ip[++w]];
        for(Re i=w+1,x=ip[w];i<=n;x=ip[i],++i){
            Re to=ip[i];v=min(v,(LL)T1.ask(x,to));
            if(A[to]<0)printf("%lld
    ",min(v,(LL)-A[to])),v-=min(v,(LL)-A[to]);
            else v+=A[to];
        }
    }
    

    【Day1 T2】多项式的运算

    传送门:多项式的运算 ( ext{[P3278]}) ( ext{[Bzoj3323]})

    【题目描述】

    你需要维护一个关于 (x) 的无穷多项式(初始时对于所有的 (i) 都有 (a_i=0)):(f(x)=a_0x^0+a_1x^1+a_2x^2...)

    现给出 (m) ((mleqslant 10^5)) 个操作,操作种类如下 ((0leqslant Lleqslant R leqslant 10^5,) (Tleqslant 10))

    • (mul L R v:)(x^L,x^{L+1}...x^R) 这些项的系数乘以 (v)

    • (add L R v:)(x^L,x^{L+1}...x^R) 这些项的系数加上 (v)

    • (mulx L R:)(x^L,x^{L+1}...x^R) 这些项乘以变量 (x)

    • (query x:) 询问 (f(x)) 的值(满足本操作只会出现 (T) 次)

    【分析】

    平衡树大水题。

    用一颗 (splay) 维护 (a_0,a_1...a_{10^5+1}) 一共 (10^5+2) 个系数,

    操作一和操作二就是个小学生懒标记的应用,

    操作三可以分为两步:在 (a_{L-1})(a_{L}) 中间插入一个 (0),然后把 (a_R) 累加到 (a_{R+1}) 头上。

    操作四可以先预处理出 (x)(0sim 10^5+1) 次幂,然后 (dfs) 中序遍历 (splay) 暴力计算答案即可。

    时间复杂度:(O(mlog n+Tn)),其中 (n=10^5+2)

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=1e5+9,P=20130426;
    int x,y,z,n=100002,T,now,ans,X[N];char op[10];
    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 Splay{
        #define pl tr[p].ps[0]
        #define pr tr[p].ps[1]
        #define pf tr[p].fa
        #define pv tr[p].v
        int O,root;queue<int>Q;
        struct QAQ{int v,fa,add,mul,size,ps[2];}tr[N];
        inline void pushup(Re p){if(p)tr[p].size=1+tr[pl].size+tr[pr].size;}
        inline void updata1(Re p,Re v){//mul
            tr[p].v=(LL)tr[p].v*v%P,tr[p].mul=(LL)tr[p].mul*v%P,tr[p].add=(LL)tr[p].add*v%P;
        }
        inline void updata2(Re p,Re v){//add
            (tr[p].v+=v)%=P,(tr[p].add+=v)%=P;
        }
        inline void pushdown(Re p){
            if(tr[p].mul!=1){
                if(pl)updata1(pl,tr[p].mul);
                if(pr)updata1(pr,tr[p].mul);
                tr[p].mul=1;
            }
            if(tr[p].add){
                if(pl)updata2(pl,tr[p].add);
                if(pr)updata2(pr,tr[p].add);
                tr[p].add=0;
            }
        }
        inline int which(Re p){return tr[pf].ps[1]==p;}
        inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
        inline void rotate(Re p){
            Re fa=pf,fas=which(p);
            Re pa=tr[fa].fa,pas=which(fa);
            Re x=tr[p].ps[fas^1];
            connect(x,fa,fas),connect(fa,p,fas^1),connect(p,pa,pas);
            pushup(fa),pushup(p);
        }
        inline void splay(Re p,Re to){
            for(Re fa;pf!=to;rotate(p))
                if(tr[fa=pf].fa!=to)rotate(which(p)==which(fa)?fa:p);
            if(!to)root=p;
        }
        inline void clear(Re p){pl=pr=pf=pv=tr[p].add=0,tr[p].mul=tr[p].size=1;}
        inline void New(Re &p,Re fa){
            if(Q.empty())p=++O;
            else p=Q.front(),Q.pop();
            clear(p),pf=fa;
        }
        inline void CL(Re p){clear(p),Q.push(p);}
        inline void build(Re &p,Re L,Re R,Re fa){
            if(L>R)return;
            Re mid=L+R>>1;New(p,fa);
            build(pl,L,mid-1,p),build(pr,mid+1,R,p);
            pushup(p);
        }
        inline int find(Re p,Re k){
            pushdown(p);
            if(tr[pl].size>=k)return find(pl,k);
            else return k<=tr[pl].size+1?p:find(pr,k-tr[pl].size-1);
        }
        inline int split(Re L,Re R){
            Re p=find(root,L-1),q=find(root,R+1);
            splay(p,0),splay(q,p);
            return tr[q].ps[0];
        }
        inline void dfs(Re p){
            pushdown(p);
            if(pl)dfs(pl);
            if(now!=-1&&pv)(ans+=(LL)pv*X[now]%P)%=P;
            ++now;
            if(pr)dfs(pr);
        }
        inline void sakura(Re L,Re R){
            Re l=find(root,L-1),r=find(root,R+1);
            splay(l,0),splay(r,l);
    
            Re p=tr[r].ps[0];
            while(pl)pushdown(p),p=pl;
            pushdown(p),New(pl,p);
            while(p)pushup(p),p=pf;
    
            p=tr[r].ps[0];
            while(pr)pushdown(p),p=pr;
            pushdown(p),(tr[r].v+=pv)%=P;
            Re fa=pf,fas=which(p);
            if(pl)connect(pl,fa,fas);
            else tr[fa].ps[fas]=0;
            CL(p),p=fa;
            while(p)pushup(p),p=pf;
        }
    }TR;
    int main(){
    //    freopen("123.txt","r",stdin);
        in(T),TR.build(TR.root,1,n+2,0);
        while(T--){
            scanf("%s",op);
            if(op[0]=='q'){
                in(x),ans=0,now=-1,X[0]=1;
                if(!x){puts("0");continue;}
                for(Re i=1;i<n;++i)X[i]=(LL)X[i-1]*x%P;
                TR.dfs(TR.root),printf("%d
    ",ans);
            }
            else{
                in(x),in(y),x+=2,y+=2;
                if(op[0]=='a')in(z),TR.updata2(TR.split(x,y),z);
                else if(strlen(op)<4)in(z),TR.updata1(TR.split(x,y),z);
                else TR.sakura(x,y);
            }
        }
    }
    

    【Day1 T3】火柴棍数字

    传送门:火柴棍数字 ( ext{[P3283]}) ( ext{[Bzoj3324]})

    【题目描述】

    给出一个长度为 (n) ((nleqslant 500)) 的十进制数 (s),现用一堆火柴棍摆成了这个数,您最多可以移动 (m) ((mleqslant 3500)) 根木棍,求最大能使这个数 (s) 变得多大。

    【分析】

    神仙 (dp)

    注意到最优方案一定是在最左边不断地放 (1),如果可操作木棍为奇数,则用剩下的一根把最左边的那个 (1) 变成 (7)

    先预处理一个转移数组 (val),其中 (val[i][j]) 表示将数字 (i) 变为 (j) 所需要的移动次数。

    (cnt[i]) 表示组成数字 (i) 所需木棍数量,则将数字 (i) 转换为 (j) 后所得到的空闲木棍数量就是 (cnt[i]-cnt[j]) 。这里的 “空闲木棍” 指的是不需要移动代价(移动代价的计算被放到了 (val) 里面),可以随便放的木棍,下同。

    (dp[i][j]) 表示目前从右至左枚举到第 (i) 位,手里有 (j) 个空闲木根,所需要的最小移动木棍次数,

    考虑枚举当前位数字 (s_i) 的转移目标 (k),设 (J=j-(cnt[s_i]-cnt[k])),则有 (dp[i][j]=min{dp[i+1][J]+val[s_i][k]]}) ((0leqslant J leqslant m))

    注意输出答案时要满足左边的尽量大,所以不能直接记录每次转移的最优决策点(因为这个“最优”是针对移动次数最小而言),而应该写一个 (dfs) 从左至右枚举,每次在所有合法决策点中找到转移目标数字最大的那一个(由于寻找的是合法决策点,所以能保证最后火柴棍的使用一定合法)。发现在这个过程中 (j) 可能为负数(因为是倒着枚举),用递推转移写起来很不方便,可以把前面 (dp) 的转移也换成 (dfs)

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

    【Code】

    (貌似应该把数组第二维开两倍处理负数的情况,但数据水直接过了,懒得去管这些细节了

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=503,M=3503,inf=1e9;
    int val[10][10]={
        {0,4,2,2,3,2,1,3,0,1},
        {0,0,2,0,0,1,1,0,0,0},
        {1,4,0,1,3,2,1,3,0,1},
        {1,3,1,0,2,1,1,2,0,0},
        {1,2,2,1,0,1,1,2,0,0},
        {1,4,2,1,2,0,0,3,0,0},
        {1,5,2,2,3,1,0,4,0,1},
        {0,1,1,0,1,1,1,0,0,0},
        {1,5,2,2,3,2,1,4,0,1},
        {1,4,2,1,2,1,1,3,0,0},
    };
    int cnt[10]={6,2,5,5,4,5,6,3,7,6};
    int n,m,dp[N][M],vis[N][M];char s[N];
    //dp[i][j]: 目前扫描到第i位,手里有j个空闲木根可以随意操作,所需要的最小移动木棍次数
    inline void in(Re &x){
        Re 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 dfs(Re i,Re j){
        if(j>m)return inf;
        if(i>n)return j?inf:0;
        if(vis[i][j])return dp[i][j];
        vis[i][j]=1,dp[i][j]=inf;
        for(Re k=9,j_,tmp;k>=0;--k)
            if((j_=j+cnt[k]-cnt[s[i]-'0'])>=0&&j_<=m)
                if((tmp=dfs(i+1,j_)+val[s[i]-'0'][k])<dp[i][j])
                    if(tmp<=m)dp[i][j]=tmp;
        return dp[i][j];
    }
    inline void print(Re i,Re j){
        if(i>n)return;
        for(Re k=9,j_;k>=0;--k)
            if(dfs(i+1,j_=j+cnt[k]-cnt[s[i]-'0'])+val[s[i]-'0'][k]<=m){
                printf("%d",k),m-=val[s[i]-'0'][k],print(i+1,j_);return;
            }
    }
    int main(){
    //    freopen("123.txt","r",stdin);
        scanf("%s%d",s+1,&m),n=strlen(s+1);
        for(Re j=m;j>1;--j)//枚举手中的空闲木棍数量
            if(dfs(1,j)<=m){
                Re now=j;
                if(now&1)putchar('7'),now-=3;
                while(now)putchar('1'),now-=2;
                print(1,j);return 0;
            }
        print(1,0);//如果不足2个就不能在前面补数,因此答案为print(1,0)而不能是print(1,1)
    }
    

    【Day2 T1】密码

    传送门:密码 ( ext{[P3279]}) ( ext{[Bzoj3325]})

    【题目描述】

    已知某长为 (n (nleqslant 10^5)) 的字符串以每个位置/空隙为中心的最长回文串长度,现需构造一个字典序最小的合法字符串。

    【分析】

    详情见另一篇博客:【题解】密码 ( ext{[SCOI2013] [P3279]})

    【Code】

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

    【Day2 T2】数数

    传送门:数数 ( ext{[P3281]}) ( ext{[Bzoj3326]})

    【题目描述】

    略。

    【分析】

    毒瘤数位 (dp),题解始终没看懂,先咕着。

    【Code】

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

    【Day2 T3】泡泡鱼

    传送门:泡泡鱼 ( ext{[P3282]}) ( ext{[Bzoj3327]})

    【题目描述】

    【分析】

    毒瘤 (OI) 又在出毒瘤计算几何了,题倒是不难,但比较麻烦,不想写。

    【Code】

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

  • 相关阅读:
    算法学习记录单链表
    算法学习记录排序
    算法学习记录图——最小生成树之prim算法
    算法学习记录图——应用之拓扑排序(Topological Sort)
    算法学习记录图(DFS BFS)
    算法学习记录排序——冒泡排序(Bubble Sort)
    算法学习记录图——最小路径之Floyd算法
    自用SqlHelper.cs
    WPF的DataGrid的标题加粗样式
    .NET Framework 4.0不能用sqlite的问题解决方法
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12711497.html
Copyright © 2011-2022 走看看