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\n",sum),0;
        if(min(a[i][0],a[i][1])>sta[n]) return printf("%lld\n",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\n",ans);
}
View Code

D - Chords

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

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

统计$x\epsilon 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

 

posted @ 2018-10-16 09:35  asd123www  阅读(500)  评论(0编辑  收藏  举报