AGC012

chrisl gjeztor Amledza
prof ovelmizer
dos carm ammeidha alzenghar
kawy may noxial gjeztor
Rupieilla vas photreywz idha

[AGC012A] AtCoder Group Contest

普及题。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,a[300010];
long long ans;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=3*n;i++)scanf("%d",&a[i]);
    sort(a+1,a+3*n+1);
    int l=1,r=3*n;
    for(int i=1;i<=n;i++){
        l++;r--;ans+=a[r];r--;
    }
    printf("%lld\n",ans);
    return 0;
}

[AGC012B] Splatter Painting

普及题。倒着扫回来。第一发 t 了,加个剪枝,存一下每个节点距离多少都被染了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,q,col[100010],dis[100010];
struct node{
    int v,next;
}edge[200010];
int t,head[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
struct Ques{
    int v,d,c;
}ques[100010];
void dfs(int x,int c,int d){
    if(!col[x])col[x]=c;
    if(!d||dis[x]>=d)return;
    dis[x]=d;
    for(int i=head[x];i;i=edge[i].next)dfs(edge[i].v,c,d-1);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    scanf("%d",&q);
    for(int i=1;i<=q;i++)scanf("%d%d%d",&ques[i].v,&ques[i].d,&ques[i].c);
    for(int i=q;i>=1;i--){
        dfs(ques[i].v,ques[i].c,ques[i].d);
    }
    for(int i=1;i<=n;i++)printf("%d\n",col[i]);
    return 0;
}

[AGC012C] Tautonym Puzzle

又是构造。感觉挺智慧的。感觉洛谷题解除了第一篇和第三篇讲 spj 怎么写的全都不如官方题解。

首先在前面顺序铺上 \(1-100\),后边铺一个排列,答案就可以变成上升子序列个数,空串也算一个。于是就要得到 \(n+1\) 个(因为算了空串)。

那么从小到大插数字。每次有两种选择:前面加一个,个数 \(+1\);后面加一个,个数 \(\times 2\)(因为算了空串所以不用 \(+1\))。那就相当于初值是 \(1\),每次操作 \(+1/\times 2\),拼到 \(n\)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
long long n;
int p[210],q[210];
int main(){
    scanf("%lld",&n);int x=0;n++;
    while(n>1){
        if(n&1)x++,p[++p[0]]=x,n--;
        else x++,q[++q[0]]=x,n>>=1;
    }
    printf("%d\n",x<<1);
    for(int i=1;i<=x;i++)printf("%d ",i);
    for(int i=1;i<=q[0];i++)printf("%d ",q[i]);
    for(int i=p[0];i>=1;i--)printf("%d ",p[i]);
    return 0;
}

[AGC012D] Colorful Balls

水题。建议和 C swap 一下。

首先看见这种东西想图上连通块。那有一个 \(O(n^2)\) 的直接连边冲的做法。

然后发现连边可以只有每个颜色最小的一个和当前颜色其他连边,每种颜色最小的和全局最小连边,然后全局最小所在颜色和全局次小颜色连边。那找到这些就可以线性了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int mod=1000000007,inf=2147483647;
int n,x,y,sum,ans=1,jc[200010],inv[200010],mn[200010],c[200010],w[200010],cnt[200010];
bool v[200010];
int main(){
    scanf("%d%d%d",&n,&x,&y);jc[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=n;i++)jc[i]=1ll*jc[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod,mn[i]=inf;
    for(int i=1;i<=n;i++)scanf("%d%d",&c[i],&w[i]),mn[c[i]]=min(mn[c[i]],w[i]);
    int minn=inf,sec=inf;
    for(int i=1;i<=n;i++){
        if(mn[i]<=minn)sec=minn,minn=mn[i];
        else if(mn[i]<=sec)sec=mn[i];
    }
    for(int i=1;i<=n;i++){
        if(mn[c[i]]+minn>y)continue;
        if(mn[c[i]]+w[i]<=x)cnt[c[i]]++,sum++;
        else if((mn[c[i]]==minn?sec:minn)+w[i]<=y)cnt[c[i]]++,sum++;
    }
    for(int i=1;i<=n;i++)ans=1ll*ans*inv[cnt[i]]%mod;
    ans=1ll*ans*jc[sum]%mod;
    printf("%d\n",ans);
    return 0;
}

[AGC012E] Camel and Oases

原先不会。后来又做了一遍还是不会。现在会了,过了样例交上去全挂。看题解发现数组开小了。

首先发现可以分成 \(\log V\) 层。然后每层可以分成若干线段。那么变成每层选一条线段,要覆盖所有位置。这个由于只有 \(\log V\) 层所以可以直接状压,设 \(pre/suf_S\) 为选了 \(S\) 内的层的最长延伸到的前 / 后缀,最后枚举第一层拼起来就行了。第一层显然最多 \(\log V\) 条线段,如果超了全 Impossible。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
#include <iostream>
#define int long long
using namespace std;
int n,V,len[21],x[200010],L[21][200010],R[21][200010],pre[1000010],suf[1000010];
signed main(){
    scanf("%lld%lld",&n,&V);
    for(int i=1;i<=n;i++)scanf("%lld",&x[i]);x[0]=-0x3f3f3f3f,x[n+1]=0x3f3f3f3f;
    for(int m=V;m;m>>=1){
        len[0]++;len[len[0]]=m;
        for(int i=1;i<=n;i++){
            if(x[i]-x[i-1]<=m)L[len[0]][i]=L[len[0]][i-1];
            else L[len[0]][i]=i;
        }
        for(int i=n;i>=1;i--){
            if(x[i+1]-x[i]<=m)R[len[0]][i]=R[len[0]][i+1];
            else R[len[0]][i]=i;
        }
    }
    len[0]++;
    for(int i=1;i<=n;i++)L[len[0]][i]=R[len[0]][i]=i;
    for(int i=1,cnt=0;i<=n;i++){
        cnt++;
        if(cnt>len[0]){
            while(n--)puts("Impossible");
            return 0;
        }
        i=R[1][i];
    }
    for(int i=0;i<(1<<len[0]);i++)suf[i]=n+1;
    for(int s=1;s<(1<<len[0]);s++){
        for(int i=1;i<=len[0];i++){
            if((s>>i-1)&1){
                pre[s]=max(pre[s],R[i][pre[s^(1<<i-1)]+1]);
                suf[s]=min(suf[s],L[i][suf[s^(1<<i-1)]-1]);
            }
        }
    }
    int U=(1<<len[0])-2;
    for(int i=1;i<=n;i++){
        int l=L[1][i]-1,r=R[1][i]+1;
        bool jud=false;
        for(int j=0;j<(1<<len[0]-1);j++){
            int s=j<<1;
            if(pre[s]>=l&&suf[U^s]<=r){
                for(int x=L[1][i];x<=R[1][i];x++)puts("Possible");
                jud=true;break;
            }
        }
        if(!jud){
            for(int x=L[1][i];x<=R[1][i];x++)puts("Impossible");
        }
        i=R[1][i];
    }
    return 0;
}

[AGC012F] Prefix Median

一看到这种数据范围小的 dp 就完全蒙掉。

首先把 \(a\) 排序。先假设两两不同。

观察一些性质。考虑每次操作是序列中插入两个 \(a\),那么当前的中位数 \(b_i\) 变成 \(b_{i+1}\) 的时候只会有三种可能:变成前驱/后继/不变。

然后考虑什么数能放在 \(b_i\) 上。放到 \(b_i\) 上的数的值域一定在 \([i,2n-i]\) 之间。

于是我们就有了如下的两条性质:对于 \(b_i\),有:

  1. \(a_i\le b_i\le a_{2n-i}\)
  2. 不存在 \(j\) 使得 \(b_i<b_j<b_{i+1}\)\(b_i>b_j>b_{i+1}\)

考虑计数。对于第一个条件,我们从后往前填数,每次选两个数放进可行集合。对于第二个条件,我们填入 \(b_i\) 的时候需要考虑所有后边的数,\(b_i\) 不能被它们的区间所包含。

这样我们就变成了这样的过程:初始有一个数 \(a\),每次向集合内加两个数,然后选择一个数 \(x\),把所有 \(a,x\) 之间的数去掉,给 \(a\) 赋值 \(x\),问最后方案数。

容易发现我们只关注当前位置左边和右边剩下多少个数。那么设 \(dp_{i,j,k}\) 为第 \(i\) 位左边 \(j\) 个右边 \(k\) 个的方案数,转移讨论向左走或者向右走。对于重复的,可以发现加入重复的点是无效的,直接忽略就行了。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
const int mod=1000000007;
int n,a[110],dp[110][110][110];
int main(){
    scanf("%d",&n);
    for(int i=1;i<(n<<1);i++)scanf("%d",&a[i]);
    sort(a+1,a+n+n);
    dp[n][0][0]=1;
    for(int i=n-1;i>=1;i--){
        int ret=(a[i]!=a[i+1]),tmp=(a[2*n-i]!=a[2*n-i-1]);
        for(int j=0;j<=(n<<1);j++){
            for(int k=0;k<=(n<<1);k++){
                dp[i][j+ret][k+tmp]=(dp[i][j+ret][k+tmp]+dp[i+1][j][k])%mod;
                for(int l=0;l<j+ret;l++)dp[i][l][k+tmp+1]=(dp[i][l][k+tmp+1]+dp[i+1][j][k])%mod;
                for(int l=0;l<k+tmp;l++)dp[i][j+ret+1][l]=(dp[i][j+ret+1][l]+dp[i+1][j][k])%mod;
            }
        }
    }
    int ans=0;
    for(int i=0;i<=(n<<1);i++)for(int j=0;j<=(n<<1);j++)ans=(ans+dp[1][i][j])%mod;
    printf("%d\n",ans);
    return 0;
}
posted @ 2023-03-18 17:07  gtm1514  阅读(82)  评论(1编辑  收藏  举报