AGC008

我突然想起来 Madoka 的剧场版第三集我当时听取了一些建议打算等到看完 12 集之后再去看。然后我现在就差这个了。

传说中让清华牛子自闭的神番。

今天好像有点困,小细节分讨签到题有点切不动的样子。

(第二天:)

观测了多头鲜花。来点读后感。

感觉在座的各位大概都可以写出这种鲜花,如果主观上想去写了。反正我没有这个勇气去写。

所以我之前的相当多的一些东西写出来就变了味了。所以说我平时看起来相当魔怔。

毕竟我不可能知道在座的各位到底是谁。

[AGC008A] Simple Calculator

我为啥这玩意交了这么多发.jpg

int x,y;
int main(){
    scanf("%d%d",&x,&y);
    if(x==y){
        puts("0");return 0;
    }
    if(!x||!y){
        if(x>y)printf("%d\n",abs(x+y)+1);
        else printf("%d\n",abs(x+y));
        return 0;
    }
    if(x>=0&&y>=0){
        if(x<y)printf("%d\n",y-x);
        else printf("%d\n",x-y+2);
    }
    else if((x>=0&&y<0)||(x<0&&y>=0)){
        printf("%d\n",min(abs(abs(y)-abs(x))+1,max(x,y)-min(x,y)));
    }
    else{
        if(x<y)printf("%d\n",y-x);
        else printf("%d\n",x-y+2);
    }
    return 0;
}

[AGC008B] Contiguous Repainting

弱智题。前面随便选点,最后一次连续就行。枚举最后一次的区间暴力算就行了。

int n,k;
long long ans,a[100010],pre[100010],suf[100010],sum[100010];
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
    for(int i=1;i<=n;i++)pre[i]=pre[i-1]+(a[i]>0?a[i]:0);
    for(int i=n;i>=1;i--)suf[i]=suf[i+1]+(a[i]>0?a[i]:0);
    for(int i=k;i<=n;i++)ans=max(ans,pre[i-k]+suf[i+1]+max(sum[i]-sum[i-k],0ll));
    printf("%lld\n",ans);
    return 0;
}

[AGC008C] Tetromino Tiling

我为啥这玩意也交了这么多发.jpg

long long ans,a[10];
int main(){
    for(int i=1;i<=7;i++)scanf("%lld",&a[i]);
    ans=a[2]+a[1]/2*2+a[4]/2*2+a[5]/2*2;
    if(a[1]&&a[4]&&a[5]){
        a[1]--;a[4]--;a[5]--;
        ans=max(ans,a[2]+3+a[1]/2*2+a[4]/2*2+a[5]/2*2);
    }
    printf("%lld\n",ans);
    return 0;
}

[AGC008D] K-th K

原先是每个数填在能填的最靠近它的位置,然后交上去挂了一半。后来想了想这样填的话后面的数会挡住前面的数在后面放的位置,而前面还没有填数。

然后换个思路,每次找 \(pos\) 最小的 \(i\) ,在最前面 \(i-1\) 个位置填上。最大的同理操作。

int n,pos[250010],a[250010];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        a[x]=pos[x]=i;
    }
    int p=1;
    for(int i=1;i<=n*n;i++){
        if(pos[i]){
            int l=a[i]-1;
            for(;l&&p<i;p++){
                if(a[p])continue;
                a[p]=a[i],l--;
            }
            if(l){
                puts("No");return 0;
            }
        }
    }
    p=n*n;
    for(int i=n*n;i>=1;i--){
        if(pos[i]){
            int r=n-a[i];
            for(;r&&p>i;p--){
                if(a[p])continue;
                a[p]=a[i],r--;
            }
            if(r){
                puts("No");return 0;
            }
        }
    }
    puts("Yes");
    for(int i=1;i<=n*n;i++)printf("%d ",a[i]);
    printf("\n");
    return 0;
}

[AGC008E] Next or Nextnext

什么世道啊,不是 sb 分讨构造就是黑。这是想让我平均一道题吃五发罚时。除了水题就是神题那我咋刷啊。(所以我压根没开VP)

居然有标签。啥 dp?数学?寄环树?那没事了。

神题,不是很懂。感觉比 F 神奇,虽然评分稍微低一点。

首先套路是排列 \(i\rightarrow p_i\) 连边。然后排列就成了一堆环。题目的两种匹配就相当于两个变换:\(i\) 连到下一个和 \(i\) 连到下一个的下一个。

这样有四种情况:

  1. 都连到下一个,就是个和原来同构的环。
  2. 都连到下一个的下一个,且环是奇环,此时还是和原来同构。
  3. 都连到下一个的下一个,且环是偶环,此时会分成两个大小为原来一半的环。
  4. 两种都有,会成基环树。(麻了我现在看见基环树就想打寄环树)

现在我们知道一个排列生成了一堆环或者基环树。分开考虑。

如果是基环树,那么我们如果要把它还原成一个环,就需要把伸出来的链缩到环里。对于相邻的两个链,前面的一个链就只能塞到这条链和下一条链之间。而且既然出现了一条链,那么链上的点一定是两两隔一个环上的点。那么根据两条链之间的长度和链长,有三种情况:塞不下,只有一种塞法,有两种塞法(链上的第一个点是否和环上的点挨着)。

如果是环,考虑逐个把环拼起来。对于每个环长 \(len\) ,个数有 \(cnt_{len}\) 个,枚举有 \(2i\) 个和其他的环拼起来。那么方案数是一堆组合数:

  1. 选出来的方案数,\(\dbinom{cnt_{len}}{2j}\)
  2. 把环拼起来的方案数,\(\dfrac{\binom{2i}{i}i!}{2^i}\)
  3. 每对环的拼法, \(len^i\)
  4. \(i\) 是奇数的时候剩下的环都有两种同构的方式, \(2^{cnt_{len}-2i}\) 。注意 \(i=1\) 的时候不能算数。

那就完了。代码好长。

const int mod=1000000007;
int n,ans=1,a[100010],ind[100010],len[100010],jc[100010],inv[100010],cnt[100010];
bool v[100010],belong[100010],in[100010];
int stk[100010],top;
void dfs1(int x){
    if(v[x]){
        if(in[x]){
            for(int i=top;i>=1;i--){
                belong[stk[i]]=true;
                if(stk[i]==x)break;
            }
        }
        return;
    }
    v[x]=in[x]=true;stk[++top]=x;
    dfs1(a[x]);
    in[x]=false;
}
bool dfs2(int x){
    if(v[x])return true;
    v[x]=true;
    stk[++top]=len[x];
    if(dfs2(a[x])&&!len[x])return true;
    return false;
}
queue<int>q;
int qpow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*ans*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
int C(int n,int m){
    if(n<m)return 0;
    return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
    scanf("%d",&n);jc[0]=inv[0]=1;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);ind[a[i]]++;
        jc[i]=1ll*jc[i-1]*i%mod;
    }
    inv[n]=qpow(jc[n],mod-2);
    for(int i=n-1;i>=1;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    for(int i=1;i<=n;i++)dfs1(i);
    for(int i=1;i<=n;i++){
        v[i]=false;
        if((belong[i]&&ind[i]>=3)||(!belong[i]&&ind[i]>=2)){
            puts("0");return 0;
        }
    }
    for(int i=1;i<=n;i++)if(!ind[i])q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();
        len[a[x]]=len[x]+1;
        if(!belong[a[x]])q.push(a[x]);
    }
    for(int i=1;i<=n;i++){
        if(!v[i]&&belong[i]){
            top=0;
            if(dfs2(i))cnt[top]++;
            else{
                int pos=0;
                for(int j=1;j<=top;j++){
                    if(stk[j]){
                        if(pos){
                            int ret=j-stk[j];
                            if(ret<pos){
                                puts("0");return 0;
                            }
                            if(ret>pos&&top>=2)ans=2ll*ans%mod;
                        }
                        pos=j;
                    }
                }
                for(int j=1;j<=top;j++){
                    if(stk[j]){
                        int ret=j-stk[j]+top;
                        if(ret<pos){
                            puts("0");return 0;
                        }
                        if(ret>pos&&top>=2)ans=2ll*ans%mod;
                        break;
                    }
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(cnt[i]){
            int ret=0;
            for(int j=0;j<=(cnt[i]>>1);j++){
                int tmp=1ll*C(cnt[i],2*j)*C(2*j,j)%mod*jc[j]%mod*qpow((mod+1)>>1,j)%mod*qpow(i,j)%mod;
                if((i&1)&&i!=1)tmp=1ll*tmp*qpow(2,cnt[i]-2*j)%mod;
                ret=(ret+tmp)%mod;
            }
            ans=1ll*ans*ret%mod;
        }
    }
    printf("%d\n",ans);
    return 0;
}

[AGC008F] Black Radius

看起来好可做啊。

首先遇到这种不知道是什么东西的东西那显然是 dp。

先看部分分全是 \(1\)

考虑把每个局面用一个比较压缩的形式表示,即设 \((x,d)\) 为在 \(x\) 处进行一次 \(d\) 距离的染色(为了方便先把整棵树都被染色的情况扔掉)。那么如果不重复计数,就只要考虑把每个相同局面中 \(d\) 最小的计入答案。

那么每个节点 \(x\) 都有一个约束条件:不能有一个相邻节点 \(y\) 满足 \((x,d)=(y,d-1)\)。显然每个节点都有一个 \(d\) 的上界。二分这个 \(d\) ,复杂度 \(O(n^2\log n)\)

不如看看这个约束条件到底影响多少点。设根为 \(x\),那么 \((x,d)\)\((y,d-1)\)\(y\) 子树内的染色范围是一样的,而在其他子树,\((y,d-1)\) 就相当于 \((x,d-2)\) 。所以考虑 \(x\) 的所有子树,\(x\)\(d\) 就是第二深的子树深度 \(+1\) 。换根即可。对了这个 \(d\) 显然小于最深的子树深度。(怎么说呢手模结果可能有点对不上,因为有个 \(2\) 的下界)

到这里我们解决了全是关键点的情况。考虑有不是关键点的问题。发现好像要整不少深度什么的东西超级难搞,于是看题解。好吊。

如果 \(x\) 不是关键点,那么假如说 \((x,d)\) 和某个关键点 \((y,d_1)\) 是等价的,观察得到只需要完全覆盖 \(y\) 所在的子树就好。这确定了下界。于是还是换根。

int n;
char s[200010];
struct node{
    int v,next;
}edge[400010];
int t,head[200010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int siz[200010],dis[200010],mx[200010],se[200010];
long long ans;
void dfs1(int x,int f){
    if(s[x]=='1')siz[x]=1;
    else dis[x]=0x3f3f3f3f;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            dfs1(edge[i].v,x);
            siz[x]+=siz[edge[i].v];
            if(mx[edge[i].v]+1>mx[x])se[x]=mx[x],mx[x]=mx[edge[i].v]+1;
            else if(mx[edge[i].v]+1>se[x])se[x]=mx[edge[i].v]+1;
            if(siz[edge[i].v])dis[x]=min(dis[x],mx[edge[i].v]+1);
        }
    }
}
void dfs2(int x,int f){
    ans+=max(0,min(mx[x],se[x]+2)-dis[x]);
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            int ret;
            if(mx[x]==mx[edge[i].v]+1)ret=se[x]+1;
            else ret=mx[x]+1;
            if(ret>mx[edge[i].v])se[edge[i].v]=mx[edge[i].v],mx[edge[i].v]=ret;
            else if(ret>se[edge[i].v])se[edge[i].v]=ret;
            if(siz[1]-siz[edge[i].v])dis[edge[i].v]=min(dis[edge[i].v],ret);
            dfs2(edge[i].v,x);
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    scanf("%s",s+1);
    dfs1(1,0);dfs2(1,0);
    printf("%lld\n",ans+1);
    return 0;
}

完了。

posted @ 2022-11-17 21:18  gtm1514  阅读(41)  评论(3编辑  收藏  举报