• AtCoder Grand Contest 028题解


    C - Min Cost Cycle

    思路好6啊,考试想了半天都没有想出来。

    一直在想一个错误的贪心算法。

    首先,我们把加一条权值为$min(Ax,By)$的边变成两条权值分别为$Ax,By$的边。

    然后点就可以分成四类$(0,0),(0,1),(1,0),(1,1)$代表入边出边是否选自己的。

    如果存在$(0,0)$,那么$(1,1)$个数一定和它相等。

    并且我们发现$(0,0)+(1,0)+(1,0)......=(0,0)$,同理$(1,1)+(0,1)$也是。

    而且$(0,0)$和相等数量的$(1,1)$可以合并成一个环,这样就一定存在合法方案。

    也就是说我们只需要保证存在一个$(1,1)$即可,直接枚举计算答案即可。

    对于不存在的直接算就行了。

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a;i<=b;++i)
    #define FOR2(a,b,i) for(int i=a;i>=b;--i)
    using namespace std;
    typedef long long ll;
    inline int read() {
        int f=1,sum=0;
        char x=getchar();
        for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
        for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
        return f*sum;
    }
    
    #define M 500005
    int n;
    int a[M][2],sta[M];
    
    int main () {
        //freopen("a.in","r",stdin);
        n=read();
        for1(1,n,i) {
            a[i][0]=read();
            a[i][1]=read();
            sta[i*2]=a[i][1];
            sta[i*2-1]=a[i][0];
        }
        sort(sta+1,sta+2*n+1);
        
        ll ans=0,sum=0;
        for1(1,n,i) ans+=a[i][0],sum+=a[i][1];
        ans=min(ans,sum),sum=0;
        for1(1,n,i) sum+=sta[i];
        for1(1,n,i) {
            if(max(a[i][0],a[i][1])<sta[n]) return printf("%lld
    ",sum),0;
            if(min(a[i][0],a[i][1])>sta[n]) return printf("%lld
    ",sum),0;
            
            ll now=sum;
            now+=max(a[i][0],a[i][1])-sta[n];
            now+=max(0,min(a[i][0],a[i][1])-sta[n-1]);
            ans=min(ans,now);
        }
        printf("%lld
    ",ans);
    }
    View Code

    D - Chords

    感觉看到这个思路之后有种被戏耍的感觉。

    我一直在想一种复杂的dp,想的我都怀疑人生了。

    统计$xepsilon S$的$Min,Max$,记做$f[Min][Max]$,直接减去分成很多块的情况就行了。

    记着尝试整体贡献拆成单个的联通块的贡献啊!!!

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a;i<=b;++i)
    #define FOR2(a,b,i) for(int i=a;i>=b;--i)
    using namespace std;
    typedef long long ll;
    inline int read() {
        int f=1,sum=0;
        char x=getchar();
        for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
        for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
        return f*sum;
    }
    
    #define M 605
    #define mod 1000000007
    int n,K;
    int pos[M],sum[M];
    int g[M],f[M][M],ans;
    
    inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;}
    
    inline void dp(int l,int r) {
        if(sum[r]-sum[l-1]&1) return;
        for1(l,r,i) if(pos[i]&&pos[i]<l||pos[i]>r) return;
        int x=g[sum[r]-sum[l-1]];
        for1(l,r-1,i) if(f[l][i]) inc(x,mod-1ll*f[l][i]*g[sum[r]-sum[i]]%mod);
        f[l][r]=x;
        inc(ans,1ll*x*g[n-2*K-sum[r]+sum[l-1]]%mod);
    }
    
    int main () {
        //freopen("a.in","r",stdin);
        n=read()*2,K=read();
        for1(1,K,i) {
            int x=read(),y=read();
            pos[x]=y,pos[y]=x;
        }
        g[0]=1;
        for(int i=2;i<=n;i+=2) g[i]=1ll*(i-1)*g[i-2]%mod;
        for1(1,n,i) sum[i]=sum[i-1]+(!pos[i]);
        for1(1,n,i) FOR2(n-i+1,1,j) dp(j,j+i-1);
        cout<<ans<<endl;
    }
    View Code

    E - High Elements

    不知道出题人为什么这么厉害。

    首先按位确定是显然的,关键在怎么$check$。

    $maxa<a1<a2......<a|a|$

    $maxb<b1<b2......<b|b|$

    $lena+|a|==lenb+|b|$

    对于原序列中的$high$,一定在分配后的序列中也是$high$。

    性质:若有解,一定存在一个$|a|,|b|$使得$|a|(|b|)$全部由$high$组成。

    如果都有不是$high$直接交换一下序列就都减少了一。

    设后面一共有$Q$个$high$,给了$b$序列$k$个,给了$b$序列$m$个非$high$。

    $lena+Q-k==lenb+k+m$,即$2*k+m==lena+Q-lenb$。

    我们可以把前面的式子看作存在一个上升序列,$high$权值为2,否则为1的权值和。

    显然若存在$max$,$max-2$一定存在,然后分奇偶维护$max$就行了。

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a;i<=b;++i)
    #define FOR2(a,b,i) for(int i=a;i>=b;--i)
    using namespace std;
    typedef long long ll;
    inline int read() {
        int f=1,sum=0;
        char x=getchar();
        for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
        for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
        return f*sum;
    }
    
    #define M 1000005
    int n;
    int vis[M];
    int a[M],f[M][2],Max[M*4][2];
    
    inline pair<int,int> query(int g,int l,int r,int lx,int rx) {
        if(lx>rx) return make_pair(0,0);
        if(lx<=l&&rx>=r) return make_pair(Max[g][0],Max[g][1]);
        pair<int,int> ans=make_pair(0,0);
        int mid=l+r>>1;
        if(lx<=mid) ans=query(g<<1,l,mid,lx,rx);
        if(rx>mid) {
            pair<int,int> y=query(g<<1|1,mid+1,r,lx,rx);
            ans.first=max(ans.first,y.first);
            ans.second=max(ans.second,y.second);
        }
        return ans;
    }
    
    inline void T_add(int g,int l,int r,int x,int id) {
        if(l==r) return Max[g][0]=f[id][0],Max[g][1]=f[id][1],void();
        int mid=l+r>>1;
        if(x<=mid) T_add(g<<1,l,mid,x,id);
        else       T_add(g<<1|1,mid+1,r,x,id);
        Max[g][0]=max(Max[g<<1][0],Max[g<<1|1][0]);
        Max[g][1]=max(Max[g<<1][1],Max[g<<1|1][1]);
    }
    
    inline bool check(int val,int to) {
        if(to<0) return 0;
        pair<int,int> y=query(1,1,n,val+1,n);
        if(!(y.first-to&1)) return y.first>=to;
        else return y.second>=to;
    }
    
    int main () {
        //freopen("a.in","r",stdin);
        n=read();
        for1(1,n,i) a[i]=read();
        
        for(int x=0,i=1;i<=n;++i) {
            x=max(x,a[i]);
            if(x==a[i]) ++vis[i];
        }
        FOR2(n,1,i) vis[i]+=vis[i+1];
        
        FOR2(n,1,i) {
            int t=1+(vis[i]!=vis[i+1]);
            pair<int,int> y=query(1,1,n,a[i]+1,n);
            f[i][y.first+t&1]=max(f[i][y.first+t&1],y.first+t);
            f[i][y.second+t&1]=max(f[i][y.second+t&1],y.second+t);
            T_add(1,1,n,a[i],i);
        }
        
        if(!check(0,vis[1])) return puts("-1"),0;
        
        for(int i=1,len[2]={0},pos[2]={0};i<=n;++i) {
            f[i][0]=f[i][1]=0;
            T_add(1,1,n,a[i],i);
            int x=a[i]>a[pos[0]]?i:pos[0],y=len[0]+(x==i);
            if(check(a[x],len[1]+vis[i+1]-y)||check(a[pos[1]],y+vis[i+1]-len[1])) {
                pos[0]=x,len[0]=y,putchar('0');
            }
            else {
                pos[1]=a[i]>a[pos[1]]?i:pos[1];
                len[1]+=pos[1]==i;
                putchar('1');
            }
        }
        puts("");
    }
    View Code

    F(F2) - Reachable Cells

    没有题解,只好看了kczno1的代码。

    智障地以为维护的是可达的第一个区间,然后就是过不去。

    只能去问本人,这才想通了。

    复杂度是$O(n^3)$的,不过常数很小,跑9s 1500挺轻松的。

    定义$f[i][j]$为$(i,j)$能到达的所有点的权值之和。

    显然只有当$(i+1,j)(i,j+1)$都没被占时我们需要减去重复的。

    $l[i][j],r[i][j]$维护的是对应$(now,j)$这个点转移完之后,在第i行可以达到的最大(小)点。

    这样判断是否有路径的交叉就可以用$(l[i][j],r[i][j])$和$(l[i][j+1],r[i][j+1])$比较了。

    显然$l[i][j]<=l[i][j+1],r[i][j]<=r[i][j+1]$。

    若$l[i][j+1]<=r[i][j]$,$(l[i][j+1],r[i][j])$一定都是数字。

    我们相当于将重复计算的点分成很多个联通块,每个联通块都存在一个点$(x,y)$可以到达剩下的所有点。

    证明的话类似$noip$引水入城,判一下路径相交就行了。

    显然$(xi,yi)$实在不断的向右下角走的,我们只需判一下这一行的交点是否大于之前的右端点就行了。

    数组两维换一下是为了卡常。。。

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a;i<=b;++i)
    #define FOR2(a,b,i) for(int i=a;i>=b;--i)
    using namespace std;
    typedef long long ll;
    
    #define M 2005
    int n;
    char a[M][M];
    int f[M][M],l[M][M],r[M][M];
    
    inline void max(int &x,int y) {if(x<y) x=y;}
    inline void min(int &x,int y) {if(x>y) x=y;}
    
    int main () {
        //freopen("a.in","r",stdin);
        scanf("%d",&n);
        for1(1,n,i) scanf("%s",a[i]+1);
        
        ll ans=0;
        FOR2(n,1,i) FOR2(n,1,j) 
            if(isdigit(a[i][j])) {
                bool t[2]={isdigit(a[i+1][j]),isdigit(a[i][j+1])};
                if(!t[0]&&!t[1]) {
                    l[j][i]=r[j][i]=j;
                    for1(i+1,n,k) l[j][k]=n+1,r[j][k]=0;
                }
                else if(!t[0]) {
                    f[i][j]=f[i][j+1];
                    l[j][i]=j,r[j][i]=r[j+1][i];
                    for1(i+1,n,k) l[j][k]=l[j+1][k],r[j][k]=r[j+1][k];
                }
                else if(!t[1]) {
                    f[i][j]=f[i+1][j];
                    l[j][i]=r[j][i]=j;
                }
                else {
                    int las=0;
                    int tot=f[i+1][j]+f[i][j+1];
                    l[j][i]=j,r[j][i]=r[j+1][i];
                    
                    for1(i+1,n,k) {
                        if(l[j+1][k]<=r[j][k]&&l[j+1][k]>las) tot-=f[k][l[j+1][k]];
                        max(las,r[j][k]);
                        min(l[j][k],l[j+1][k]);
                        max(r[j][k],r[j+1][k]); 
                    }
                    f[i][j]=tot;
                }
                ans+=f[i][j]*(a[i][j]-'0');
                f[i][j]+=a[i][j]-'0';
            }
            else l[j][i]=n+1,r[j][i]=0;
        cout<<ans<<endl;
    }
    View Code
  • 相关阅读:
    onclick中的函数的参数this
    classList的使用
    设置点击鼠标时不跳转
    模块补充shutil,logging
    re模块拾遗和递归函数
    正则表达式-re模块
    软件开发规范
    自定义模块2
    常用模块
    初识自定义模块
  • 原文地址:https://www.cnblogs.com/asd123www/p/9796128.html
Copyright © 2020-2023  润新知