zoukankan      html  css  js  c++  java
  • USACO 乱做

    cnyz 水平太菜,只配做 pj 难度的题了。

    USACO 2021 Open G T1

    Solution

    类似采花?

    不难想到对每个位置记录一个 \(pre_i\) 表示在它之前且离它最近与其相同品种的位置。

    对于每一个 \(r\),我们考虑在它之前可能的方案数,也就是在 \([pre_r+1,r-1]\) 的这样一个区间,\(pre_r+1\) 是因为我们一定不能包含 \(pre_r\) 这个位置,\(r-1\) 是因为题目要求了区间长度至少为 \(2\)

    我们暂时无法满足 \(l\) 的限制,但是类似采花,我们每次只保留最靠右的位置,即对于每一个求过答案的 \(r\),删除 \(pre_r\),使其不能作为答案。

    直接使用线段树优化,时间复杂度 \(O(n\log n)\)

    My Code
    const int N=2e5;
    ll sum[N*4+10],ans,orz;
    void update(int p,int l,int r,int x,int v) {
        if(l==r) {sum[p]+=v;return;}
        int mid=(l+r)>>1;
        if(x<=mid) update(p*2,l,mid,x,v);
        else update(p*2+1,mid+1,r,x,v);
        sum[p]=sum[p*2]+sum[p*2+1];
    }
    ll query(int p,int l,int r,int x,int y) {
        if(x>y) return 0;
        if(x<=l&&r<=y) return sum[p];
        int mid=(l+r)>>1,ret=0;
        if(x<=mid) ret+=query(p*2,l,mid,x,y);
        if(y>mid) ret+=query(p*2+1,mid+1,r,x,y);
        return ret;
    }
    int n,a[N+10],pre[N+10],tmp[N+10];
    int main() {
        n=read();
        for(int i=1;i<=n;i++) {
            a[i]=read();
            pre[i]=tmp[a[i]];
            tmp[a[i]]=i;
        }
        update(1,0,n,0,1);
        for(int i=1;i<=n;i++) {
            ans+=query(1,0,n,pre[i],i-2);
            update(1,0,n,i,1);
            if(pre[i]) update(1,0,n,pre[i]-1,-1);
        }
        printf("%lld\n",ans);
    }
    

    USACO 2021 Open G T2

    Solution

    对于每一个点拆点,拆成 \(4\) 个点分别表示四个传送门,将传送门转为边,同时相邻两点也转成边,此时图会被划分成若干个连通块。

    不难发现每一次操作,我们都会将两个连通块进行连通,其费用为 \(c_i\)

    那么问题变成了最小生成树,可以直接贪心。

    时间复杂度 \(O(n\log n)\)

    My Code
    const int N=1e5;
    int n,c[N+10],t[N+10][5],ans;
    vector<int> G[N*4+10],zz[N*2+10];
    int vis[N*4+10],tot;
    void dfs(int u,int rt) {
        vis[u]=rt;
        for(auto v:G[u])
            if(!vis[v])
                dfs(v,rt);
    }
    int fa[N+10],etot;
    struct node {
        int x,y,v;
    } E[N+10];
    bool cmp(node _,node __) {
        return _.v<__.v;
    }
    int find(int x) {
        if(x==fa[x]) return x;
        return fa[x]=find(fa[x]);
    }
    int main() {
        n=read();
        for(int i=1;i<=n;i++) {
            c[i]=read();
            G[(i-1)*4].pb((i-1)*4+1);
            G[(i-1)*4+1].pb((i-1)*4);
            G[(i-1)*4+2].pb((i-1)*4+3);
            G[(i-1)*4+3].pb((i-1)*4+2);
            for(int j=1;j<=4;j++) t[i][j]=read(),zz[t[i][j]].pb((i-1)*4+j-1);
        }
        for(int i=1;i<=2*n;i++) G[zz[i][0]].pb(zz[i][1]),G[zz[i][1]].pb(zz[i][0]);
        for(int i=1;i<=n;i++) {
            if(!vis[(i-1)*4]) dfs((i-1)*4,++tot);
            if(!vis[(i-1)*4+1]) dfs((i-1)*4+1,++tot);
            if(!vis[(i-1)*4+2]) dfs((i-1)*4+2,++tot);
            if(!vis[(i-1)*4+1]) dfs((i-1)*4+3,++tot);
            if(vis[(i-1)*4]!=vis[(i-1)*4+2]) E[++etot]={vis[(i-1)*4],vis[(i-1)*4+2],c[i]};
        }
        sort(E+1,E+etot+1,cmp);
        // for(int i=1;i<=etot;i++) printf("%d %d %d\n",E[i].x,E[i].y,E[i].v);
        // printf("%d\n",tot);
        for(int i=1;i<=tot;i++) fa[i]=i;
        for(int i=1;i<=etot;i++) {
            int fx=find(E[i].x),fy=find(E[i].y);
            if(fx==fy) continue;
            ans+=E[i].v,fa[fx]=fy;
        }
        write(ans);enter;
    }
    

    USACO 2021 Open G T3

    Solution

    不难发现最后形成图形的凸包一定是一个三角形,考虑维护这个三角形。

    想到一个 dp,设 \(f_{i,j,k,w}\) 表示三角形的三个点是 \(p_i,p_j,p_k\),还有 \(w\) 个点没选的方案数。

    我们思考这个加点的过程,发现一个点要想被加进来,必须满足如下两个条件:

    1. 其在三角形内。
    2. 延长三角形三边,其在三角形对角所覆盖的范围内。

    发现第二种情况完全不会判,怎么办呢,不判。

    对,不判,我们直接记忆化搜索,从最大的那个三角形开始搜起,每次枚举一个在三角形内的点,递归下去并转移。

    这样看起来时间复杂度是 \(O(n^7)\) 的,但常数很小,总之就是过了。

    具体实现细节见代码:

    My Code
    const int N=50,mod=1e9+7;
    const double pi=acos(-1),eps=1e-8;
    void Add(int &x,int y) {
        x+=y;
        if(x>=mod) x-=mod;
    }
    struct Vector {
        int x,y;
        double len() {return sqrt(1.0*x*x+1.0*y*y);}
        int operator *(Vector _) {return x*_.x+y*_.y;}
    };
    int n;
    int f[N+10][N+10][N+10][N+10],in[N+10][N+10][N+10];
    int x[N+10],y[N+10],fac[N+10],C[N+10][N+10];
    bool vis[N+10][N+10][N+10];
    double angle(int a,int b,int c) {
        Vector A={x[b]-x[a],y[b]-y[a]};
        Vector B={x[c]-x[a],y[c]-y[a]};
        return acos(1.0*(A*B)/A.len()/B.len());
    }
    bool check(int a,int b,int c,int v) {
        //printf("chk %d,%d,%d,%d,result: %d\n",a,b,c,v,fabs(angle(v,a,b)+angle(v,b,c)+angle(v,a,c)-2*pi)<eps);
        return fabs(angle(v,a,b)+angle(v,b,c)+angle(v,a,c)-2*pi)<eps;
    }
    void dp(int a,int b,int c) {
        if(vis[a][b][c]) return;
        // printf("dp(%d,%d,%d)\n",a,b,c);
        vis[a][b][c]=1;
        for(int i=0;i<=in[a][b][c];i++) {
            Add(f[a][b][c][i],6ll*C[in[a][b][c]][i]%mod*fac[in[a][b][c]-i]%mod);
            // printf("f[%d] is %d\n",i,f[a][b][c][i]);
            // //f[i][j][k][t]=6LL*C[u[i][j][k]][t]%P*fac[u[i][j][k]-t]%P;
        }
        int tmp[3];
        for(int i=1;i<=n;i++)
            if(a!=i&&b!=i&&c!=i&&check(a,b,c,i)) {
                tmp[0]=a,tmp[1]=b,tmp[2]=i,sort(tmp,tmp+3),dp(tmp[0],tmp[1],tmp[2]);
                for(int q=0;q<=in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1;q++)
                    for(int w=0;w<=in[tmp[0]][tmp[1]][tmp[2]];w++)
                        for(int t=0;t<=w;t++)
                            Add(f[a][b][c][q+t],1ll*f[tmp[0]][tmp[1]][tmp[2]][w]*
                                fac[in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1-q+w-t]%mod*
                                C[in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1][q]%mod*C[w][t]%mod);
                /*
                rep(q,0,in[a][b][v]-in[tmp[0]][tmp[1]][tmp[2]]-1)
                rep(w,0,in[tmp[0]][tmp[1]][tmp[2]])
                rep(t,0,w)
                f[a][b][c][q+t]=(f[i][j][k][q+t]+1LL*f[c[0]][c[1]][c[2]][w]*
                                fac[u[i][j][k]-u[c[0]][c[1]][c[2]]-1-q+w-t]%P*
                                C[u[i][j][k]-u[c[0]][c[1]][c[2]]-1][q]%P*C[w][t])%P;
                */
                tmp[0]=a,tmp[1]=c,tmp[2]=i,sort(tmp,tmp+3),dp(tmp[0],tmp[1],tmp[2]);
                for(int q=0;q<=in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1;q++)
                    for(int w=0;w<=in[tmp[0]][tmp[1]][tmp[2]];w++)
                        for(int t=0;t<=w;t++)
                            Add(f[a][b][c][q+t],1ll*f[tmp[0]][tmp[1]][tmp[2]][w]*
                                fac[in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1-q+w-t]%mod*
                                C[in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1][q]%mod*C[w][t]%mod);
                tmp[0]=b,tmp[1]=c,tmp[2]=i,sort(tmp,tmp+3),dp(tmp[0],tmp[1],tmp[2]);
                for(int q=0;q<=in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1;q++)
                    for(int w=0;w<=in[tmp[0]][tmp[1]][tmp[2]];w++)
                        for(int t=0;t<=w;t++)
                            Add(f[a][b][c][q+t],1ll*f[tmp[0]][tmp[1]][tmp[2]][w]*
                                fac[in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1-q+w-t]%mod*
                                C[in[a][b][c]-in[tmp[0]][tmp[1]][tmp[2]]-1][q]%mod*C[w][t]%mod);
            }
    }
    int main() {
        n=read();
        for(int i=1;i<=n;i++) x[i]=read(),y[i]=read();
        fac[0]=1;
        for(int i=1;i<=N;i++) fac[i]=1ll*fac[i-1]*i%mod;
        C[0][0]=1;
        for(int i=1;i<=N;i++) {
            C[i][0]=1,C[i][i]=1;
            for(int j=1;j<i;j++)
                Add(C[i][j],C[i-1][j]),
                Add(C[i][j],C[i-1][j-1]);
        }
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                for(int k=j+1;k<=n;k++)
                    for(int t=1;t<=n;t++)
                        if(t!=i&&t!=j&&t!=k&&check(i,j,k,t))
                            in[i][j][k]++;
        // for(int i=1;i<=n;i++)
        //     for(int j=i+1;j<=n;j++)
        //         for(int k=j+1;k<=n;k++)
        //             printf("in[%d][%d][%d] is %d\n",i,j,k,in[i][j][k]);
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                for(int k=j+1;k<=n;k++)
                    if(in[i][j][k]==n-3) {
                        dp(i,j,k);
                        write(f[i][j][k][0]),enter;
                        return WDNMD;
                    }
        write(0),enter;
        return WDNMD;
    }
    

    USACO 2021 Open P T1

    Solution

    银组加强版,同样考虑记录 \(pre_i\)

    但是会发现一件事情,不同的领队算不同的方案,那么此时方案就有点难算了,但还是继承 G 组 T1 的想法。

    同样的,答案就是 \([pre_r+1,i-2]\),考虑更新。

    更新就是,我们发现 \([pre_{pre_r}+1,pre_r-1]\) 这些位置已经不再可能成为以 \(r\) 这个位置作为领队的答案,区间减一,同时 \([pre_r+1,i-1]\) 可以成为以 \(r\) 这个位置作为领队的答案,区间加一。

    注意我们并未处理 \(pre_r\) 的情况,此时我们发现,无论如何,\(pre_r\) 绝对不会成为答案,直接删除这个节点。

    使用线段树维护,时间复杂度 \(O(n\log n)\)

    My Code
    const int N=2e5;
    int sum[N*4+10],add[N*4+10],ban[N*4+10];
    void pushup(int p) {
        sum[p]=sum[p*2]+sum[p*2+1];
        ban[p]=ban[p*2]+ban[p*2+1];
    }
    void addadd(int p,int l,int r,int v) {
        add[p]+=v;
        sum[p]+=1ll*v*(r-l+1-ban[p]);
    }
    void pushdown(int p,int l,int r) {
        int mid=(l+r)>>1;
        if(add[p]) {
            addadd(p*2,l,mid,add[p]);
            addadd(p*2+1,mid+1,r,add[p]);
        }
        add[p]=0;
    }
    void update_add(int p,int l,int r,int x,int y,int v) {
        if(x>y) return;
        if(ban[p]==r-l+1) return;
        if(x<=l&&r<=y) {
            addadd(p,l,r,v);
            return;
        }
        pushdown(p,l,r);
        int mid=(l+r)>>1;
        if(x<=mid) update_add(p*2,l,mid,x,y,v);
        if(y>mid) update_add(p*2+1,mid+1,r,x,y,v);
        pushup(p);
    }
    void update_ban(int p,int l,int r,int x) {
        if(x==0) return;
        if(ban[p]==r-l+1) return;
        if(l==r) {
            ban[p]=1;sum[p]=0;
            return;
        }
        pushdown(p,l,r);
        int mid=(l+r)>>1;
        if(x<=mid) update_ban(p*2,l,mid,x);
        else update_ban(p*2+1,mid+1,r,x);
        pushup(p);
    }
    int query(int p,int l,int r,int x,int y) {
        if(x>y) return 0;
        if(ban[p]==r-l+1) return 0;
        if(x<=l&&r<=y) return sum[p];
        pushdown(p,l,r);
        int mid=(l+r)>>1,ret=0;
        if(x<=mid) ret+=query(p*2,l,mid,x,y);
        if(y>mid) ret+=query(p*2+1,mid+1,r,x,y);
        return ret;
    }
    int n,a[N+10],tmp[N+10],pre[N+10];
    ll ans;
    int main() {
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=1;i<=n;i++) {
            pre[i]=tmp[a[i]];
            tmp[a[i]]=i;
            // printf("%d ",pre[i]);
        }
        // puts("");
        for(int i=1;i<=n;i++) {
            ans+=query(1,1,n,pre[i]+1,i-2);
            // printf("Add query %d,%d,%d\n",pre[i]+1,i-2,query(1,1,n,pre[i]+1,i-2));
            update_add(1,1,n,pre[pre[i]]+1,pre[i]-1,-1);
            // printf("Add %d,%d,%d\n",pre[pre[i]]+1,pre[i]-1,-1);
            update_add(1,1,n,pre[i]+1,i-1,1);
            // printf("Add %d,%d,%d\n",pre[i]+1,i-1,1);
            update_ban(1,1,n,pre[i]);
            // printf("Ban %d\n",pre[i]);
        }
        printf("%lld\n",ans);
        return 0;
    }
    

    USACO 2021 Open P T3

    Solution 设 $f_{i,l,r,0/1,0/1}$ 表示第 $i$ 行我取的区间是 $[l,r]$,此时 $l/r$ 处于扩张/缩小状态。

    这玩意暴力枚举是 \(O(n^5)\) 的,然而可以直接二维前缀和,于是变成了 \(O(n^3)\) 没了。

    注意转移可能要细心的推一下。

    My Code
    const int N=150,mod=1e9+7;
    int n,sg[N+10],f[N+10][N+10][N+10][2][2],g[N+10][N+10][N+10][2][2],ans;
    char ch[N+10];
    void Del(int &x,int y) {
        x-=y;
        if(x<0) x+=mod;
    }
    void Add(int &x,int y) {
        x+=y;
        if(x>=mod) x-=mod;
    }
    int gt(int i,int a,int x,int b,int y,int l,int r) {
        int res=0;
        Add(res,g[i][x][y][l][r]);
        Del(res,g[i][a-1][y][l][r]);
        Del(res,g[i][x][b-1][l][r]);
        Add(res,g[i][a-1][b-1][l][r]);
        return res;
    }
    int main() {
        n=read();
        for(int i=1;i<=n;i++) {
            gs(ch+1);
            for(int j=1;j<=n;j++) sg[j]=sg[j-1]+(ch[j]=='G');
            for(int l=1;l<=n;l++)
                for(int r=l;r<=n;r++)
                    if(sg[r]-sg[l-1]==r-l+1) {
                        Add(f[i][l][r][0][0],gt(i-1,l,r,l,r,0,0));
                        Add(f[i][l][r][0][0],1);
                        Add(f[i][l][r][1][0],gt(i-1,1,l,l,r,1,0));
                        Add(f[i][l][r][1][0],gt(i-1,1,l-1,l,r,0,0));
                        Add(f[i][l][r][0][1],gt(i-1,l,r,r,n,0,1));
                        Add(f[i][l][r][0][1],gt(i-1,l,r,r+1,n,0,0));
                        Add(f[i][l][r][1][1],gt(i-1,1,l-1,r+1,n,0,0));
                        Add(f[i][l][r][1][1],gt(i-1,1,l,r+1,n,1,0));
                        Add(f[i][l][r][1][1],gt(i-1,1,l-1,r,n,0,1));
                        Add(f[i][l][r][1][1],gt(i-1,1,l,r,n,1,1));
                        Add(ans,f[i][l][r][0][0]);
                        Add(ans,f[i][l][r][0][1]);
                        Add(ans,f[i][l][r][1][0]);
                        Add(ans,f[i][l][r][1][1]);
                    }
            for(int l=1;l<=n;l++)
                for(int r=1;r<=n;r++)
                    for(int x=0;x<=1;x++)
                        for(int y=0;y<=1;y++)
                            Add(g[i][l][r][x][y],f[i][l][r][x][y]),
                            Add(g[i][l][r][x][y],g[i][l-1][r][x][y]),
                            Add(g[i][l][r][x][y],g[i][l][r-1][x][y]),
                            Del(g[i][l][r][x][y],g[i][l-1][r-1][x][y]);
        }
        write(ans),enter;
        return WDNMD;
    }
    

    USACO 2021 Open P T2

    矩阵树定理,决定先鸽。

  • 相关阅读:
    WCF 第四章 绑定 在多个绑定上暴露一个服务契约
    WCF 第五章 行为 事务跨操作事务流
    WCF 第五章 导出并发布元数据(服务行为)
    WCF 第五章 行为 通过配置文件暴露一个服务行为
    WCF 第五章 不支持会话的绑定的默认并发和实例
    WCF 第五章 并发和实例(服务行为)
    WCF 第五章 行为 总结
    WCF 第四章 绑定 绑定元素
    WCF 第五章 行为 事务之选择一个事务协议OleTx 或者WSAT
    WCF 第四章 绑定 比较各种绑定的性能和可扩展性
  • 原文地址:https://www.cnblogs.com/cnyzz/p/15411394.html
Copyright © 2011-2022 走看看