首页 > 其他分享 >[VP记录]AGC001

[VP记录]AGC001

时间:2022-11-10 21:45:38浏览次数:66  
标签:AGC001 return 记录 int mid long VP edge ans

[AGC001A] BBQ Easy

普及题。排序后把奇数位置上的加上就好了。

int n,a[100010],ans;
int main(){
    scanf("%d",&n);n<<=1;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i+=2)ans+=a[i];
    printf("%d\n",ans);
}

[AGC001B] Mysterious Light

不是很普及的找规律题。

发现轨迹是若干个等边三角形,所以考虑把它们拼成一个大的等边三角形然后找边长。每次反射相当于把边长变成 \(\gcd(n-x,x)\) ,然后根据这个 \(\gcd\) 手模几组数据打个表可以发现边长是 \(n-\gcd(n,x)\)。

long long n,x;
long long gcd(long long a,long long b){
    return b?gcd(b,a%b):a;
}
int main(){
    scanf("%lld%lld",&n,&x);
    printf("%lld\n",3*(n-gcd(n,x)));
    return 0;
}

[AGC001C] Shorten Diameter

一看这个 \(2000\) 的数据范围就可以枚举每个点跑 dfs。

奇偶讨论一下 \(k\),如果是奇数那么就枚举中间的一条边,两边往下 dfs \(\dfrac k2\) 的深度。如果是偶数那么就枚举中间的一个点,然后搜 \(\dfrac k2\) 的深度。搜到的点数就是能保留的点数,取个最大值减一下就行了。

struct node{
    int v,next;
}edge[4010];
int n,k,t,ans,cnt,head[2010],fa[2010];
bool v[2010][2010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
void dfs1(int x,int f){
    fa[x]=f;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f)dfs1(edge[i].v,x);
    }
}
void dfs(int x,int f,int dep){
    if(!(~dep))return;
    cnt++;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f)dfs(edge[i].v,x,dep-1);
    }
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs1(1,0);
    if(k&1){
        for(int i=2;i<=n;i++){
            cnt=0;
            dfs(fa[i],i,k>>1);
            dfs(i,fa[i],k>>1);
            ans=max(ans,cnt);
        }
    }
    else{
        for(int i=1;i<=n;i++){
            cnt=0;
            dfs(i,0,k>>1);
            ans=max(ans,cnt);
        }
    }
    printf("%d\n",n-ans);
    return 0;
}

[AGC001D] Arrays and Palindrome

模拟赛题。赛时由于并没有捉摸透题面的语文含义输出了个 \(-1\)。洛谷的翻译就好很多。

抽象成图论模型就是在 \(n\) 个点之间回文的对应位置连边,最后使得整张图连通。

首先特判无解,是 \(A\) 中奇数大于等于三个的情况。由于每个奇数会使得中间一个点少连一条边,度数少 \(1\),而整张图的度数最少是 \(2n-2\)(一棵树)。然后把两个奇数一个开头一个结尾就是 \(a\) 数组,开头减一结尾加一,去掉前导 \(0\)就是 \(b\) 数组。

int n,m,a[100010];
int main(){
    scanf("%d%d",&n,&m);
    int cnt=0;
    for(int i=1;i<=m;i++)scanf("%d",&a[i]),cnt+=(a[i]&1);
    if(cnt>2){
        puts("Impossible");return 0;
    }
    for(int i=m;i>=1;i--)if(a[i]&1){
        swap(a[i],a[m]);break;
    }
    for(int i=1;i<=m;i++)if(a[i]&1){
        swap(a[i],a[1]);break;
    }
    for(int i=1;i<=m;i++)printf("%d ",a[i]);printf("\n");
    if(m==1&&a[1]!=1)m++;
    a[1]--;a[m]++;
    cnt=1;
    for(int i=1;i<=m;i++)if(!a[i])m--,cnt++;
    printf("%d\n",m);
    for(int i=cnt;i<=m+cnt-1;i++)printf("%d ",a[i]);
    return 0;
}

[AGC001E] BBQ Hard

套路题。但是忘了套路。看到题解第一眼发现我是sb那没事了。

忘了这个套路的可以看学长馈赠 1 的 T3。

实际上这个组合数可以长得像格路计数一样,一个点的坐标就是 \((a_i,b_i)\)。然后就可以 \(dp\) 。 \(dp_{i,j}\) 为到 \((i,j)\) 的方案数,转移 \(dp_{i,j}=dp_{i-1,j}+dp_{i,j-1}\)。

然后看到值域很小,考虑在值域上下点功夫。又看到这个两个点坐标相加的形式,容易想到把一个点的坐标变成 \((-a_i,-b_i)\),然后每个点对应坐标的 \(dp\) 值 \(+1\),然后跑一遍就可以得到答案。记得去掉自己对自己的贡献然后除以 \(2\) 。

const int mod=1000000007;
int n,ans,dp[5010][5010],a[200010],b[200010],jc[10010],inv[10010];
const int mx=2005;
int C(int n,int m){
    return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
}
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 main(){
    scanf("%d",&n);jc[0]=inv[0]=1;
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]),dp[-a[i]+mx][-b[i]+mx]++;
    for(int i=1;i<=4*mx;i++)jc[i]=1ll*jc[i-1]*i%mod;
    inv[mx<<2]=qpow(jc[mx<<2],mod-2);
    for(int i=(mx<<2)-1;i>=1;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    for(int i=1;i<=2*mx;i++){
        for(int j=1;j<=2*mx;j++){
            (dp[i][j]+=dp[i-1][j]+dp[i][j-1])%=mod;
        }
    }
    for(int i=1;i<=n;i++){
        ans=(1ll*ans+dp[a[i]+mx][b[i]+mx]-C(2*a[i]+2*b[i],2*a[i])+mod)%mod;
    }
    ans=1ll*ans*((mod+1)>>1)%mod;
    printf("%d\n",ans);
    return 0;
}

[AGC001F] Wide Swap

很玄妙的题。第一步把我卡住了。

考虑怎么把这个变换整成看着舒服点的形式。可以把 \(P\) 的下标和值调换得到排列 \(Q\),然后变换就变成了对于 \(|Q_i-Q_{i+1}|\ge k\) 可以调换。长的就很像冒泡排序。

然后实际上你把冒泡换成归并就可以过了。归并的时候记录前面 \([l,mid]\) 的后缀最小值 \(minn\),如果对右面的 \(j\) 有 \(minn_i-Q_j\ge k\) 就可以把右边归并上去。

int n,k,a[500010],b[500010],minn[500010];
void merge(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    merge(l,mid);merge(mid+1,r);
    minn[mid]=a[mid];
    for(int i=mid-1;i>=l;i--)minn[i]=min(minn[i+1],a[i]);
    int i=l,j=mid+1,pos=l;
    while(i<=mid&&j<=r){
        if(minn[i]-a[j]>=k)b[pos]=a[j],j++;
        else b[pos]=a[i],i++;
        pos++;
    }
    while(i<=mid)b[pos]=a[i++],pos++;
    while(j<=r)b[pos]=a[j++],pos++;
    for(int i=l;i<=r;i++)a[i]=b[i];
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);a[x]=i;
    }
    merge(1,n);
    for(int i=1;i<=n;i++)b[a[i]]=i;
    for(int i=1;i<=n;i++)printf("%d\n",b[i]);
    return 0;
}

完。

标签:AGC001,return,记录,int,mid,long,VP,edge,ans
From: https://www.cnblogs.com/gtm1514/p/16878893.html

相关文章