• 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(nlog 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
    ",ans);
    }
    

    USACO 2021 Open G T2

    Solution

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

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

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

    时间复杂度 (O(nlog 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
    ",E[i].x,E[i].y,E[i].v);
        // printf("%d
    ",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
    ",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)
    ",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
    ",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
    ",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(nlog 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
    ",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
    ",pre[pre[i]]+1,pre[i]-1,-1);
            update_add(1,1,n,pre[i]+1,i-1,1);
            // printf("Add %d,%d,%d
    ",pre[i]+1,i-1,1);
            update_ban(1,1,n,pre[i]);
            // printf("Ban %d
    ",pre[i]);
        }
        printf("%lld
    ",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

    矩阵树定理,决定先鸽。

  • 相关阅读:
    实现一个可host asp.net程序的小型IIS(Cassinidev介绍)
    json数组对象和对象数组
    select 操作大全动态增中值
    jquery将某些ID显示出来
    如何合理利用好nofollow标签
    javascript读写COOKS
    [PHPNow] 使用PHPNOW常见的10个问题
    repeate 常用的每行显示几个共几行
    java学习之面向对象(this,static,pakage,import)
    Java学习之面向对象(1.Object类,(1)toString方法,(2)equals方法,2.对象转型(casting),3.动态绑定与多态,4.抽象类(abstract),5.Final关键字)
  • 原文地址:https://www.cnblogs.com/lajiccf/p/15411394.html
Copyright © 2020-2023  润新知