AGC011

敬告各位:大佬魔怔那叫乐呵,如果实力不够还魔怔那叫小丑。

这其实和洛谷灌水区是一个道理,现在灌水区不是流汗就是流汗。虽然有几个真正提问的。

[AGC011A] Airport Bus

普及题。排序贪心扫一遍。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,c,k,ans,a[100010];
int main(){
    scanf("%d%d%d",&n,&c,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    int cnt=0,t=0;
    for(int i=1;i<=n;i++){
        if(cnt>=c||a[i]>t)t=a[i]+k,cnt=1,ans++;
        else cnt++;
    }
    printf("%d\n",ans);
    return 0;
}

[AGC011B] Colorful Creatures

普及题。贪心合并。开 long long。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
int n,ans,a[100010];
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        if(a[i+1]<=2*a[i])ans++;
        else ans=0;
        a[i+1]+=a[i];
    }
    printf("%lld\n",ans);
    return 0;
}

[AGC011C] Squared Graph

这玩意我真不会。感觉 D 和 E 要比这题简单很多。

考虑两对点 \((a,b),(c,d)\) 在同一个连通块内的条件: \(a\rightarrow c,b\rightarrow d\) 路径的奇偶性相同。

那么可以分每个连通块分别考虑。如果连通块有奇环,那么连通块内任意两个点都有一条长度为奇的路径和一条长度为偶的路径,即自己有 \(1\) 的贡献。如果没有,那么奇偶分开,自己有 \(2\) 的贡献。特殊处理所有单独的点,自己有 \(1\) 的贡献。

考虑连通块间的贡献。顺序考虑有奇环/无奇环/单点。有奇环的之间会产生 \(2\) 的贡献。无奇环的之间有 \(4\) 的贡献,会和有奇环的产生 \(2\) 的贡献。单点会和剩下的所有节点产生 \(2\) 的贡献。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,a,b,c,col[100010];
struct node{
    int v,next;
}edge[400010];
int t,head[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
bool dfs(int x,int c){
    col[x]=c;
    bool jud=true;
    for(int i=head[x];i;i=edge[i].next){
        if(col[edge[i].v]){
            if(col[edge[i].v]==col[x])jud=false;
        }
        else jud&=dfs(edge[i].v,3-c);
    }
    return jud;
}
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);
    }
    for(int i=1;i<=n;i++){
        if(col[i])continue;
        if(!head[i])c++;
        else{
            if(dfs(i,1))b++;
            else a++;
        }
    }
    long long ans=0;
    int cnt=0;
    for(int i=1;i<=a;i++)ans+=2*cnt+1,cnt++;
    for(int i=1;i<=b;i++)ans+=2*cnt+2,cnt+=2;
    cnt=n-c;
    for(int i=1;i<=c;i++)ans+=2*cnt+1,cnt++;
    printf("%lld\n",ans);
    return 0;
}

[AGC011D] Half Reflector

手模样例题。

手模样例可以得到一次操作的变化规律:如果开头是 A,那改成 B。如果开头是 B,那么把开头去掉,后边的串 AB 翻转,最后加个 A。

接着模可以得到另一个结论:所有的最后都会变成 BABABABA\(\cdots\) 的样子。如果串长是偶数那就固定了。如果是奇数那第一个会在 AB 之间来回变。显然最多需要 \(2n\) 次变成这样。

那直接模拟复杂度就是 \(O(n)\) 的了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,k,a[800010];
char s[200010];
int main(){
    scanf("%d%d%s",&n,&k,s+1);
    for(int i=1;i<=n;i++)a[i]=s[i]-'A';
    int od=0,l=1;
    for(int i=1;i<=min(k,n<<1);i++){
        if((a[l]^od)==0)a[l]^=1;
        else od^=1,l++,a[l+n-1]=od;
    }
    k-=min(k,n<<1);
    if((k&1)&&(n&1))a[l]^=1;
    for(int i=l;i<=l+n-1;i++)putchar((a[i]^od)+'A');
    puts("");
    return 0;
}

[AGC011E] Increasing Numbers

小清新数学题。也不是很数学。

首先每个递增的数都可以变成形如 \(\sum_{i=1}^9\dfrac{10^{a_{i}}-1}9\) 的形式。然后设答案是 \(cnt\),有

\[n=\sum_{i=1}^{cnt}\sum_{j=1}^9\dfrac{10^{a_{i,j}}-1}9 \]

\[9n+9cnt=\sum_{i=1}^{cnt}\sum_{j=1}^910^{a_{i,j}} \]

看看右边是什么东西。如果右边 \(a_{i,j}\) 互不相同,那么显然数位和是 \(9cnt\)。如果有重复,一次进位就是 \(-9\)。也就是说只要 \(9n+9cnt\) 的数位和 \(\le 9cnt\) 就行了。\(cnt\) 的上界显然是 \(n\)。高精暴力算,复杂度 \(O(n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n;
struct bignum{
    int a[1000010],sum;
    bignum(){a[0]=1;}
    void plus(int n){
        int x=n;
        for(int i=1;x;i++){
            sum-=a[i];
            a[i]+=x;
            x=a[i]/10;a[i]%=10;
            sum+=a[i];
            if(a[0]<i)a[0]=i;
        }
    }
    void mul(int n){
        int x=0;
        for(int i=1;i<=a[0]||x;i++){
            a[i]=a[i]*n+x;
            x=a[i]/10;a[i]%=10;
            if(a[0]<i)a[i]=i;
        }
    }
}a;
int b;
char s[500010];
int main(){
    scanf("%s",s+1);n=strlen(s+1);
    for(int i=n;i>=1;i--)a.a[n-i+1]=s[i]-'0';
    a.a[0]=n;
    a.mul(9);
    for(int i=1;i<=a.a[0];i++)a.sum+=a.a[i];
    for(int k=1;k<=n;k++){
        a.plus(9);b+=9;
        if(a.sum<=b){
            printf("%d\n",k);
            break;
        }
    }
    return 0;
}

[AGC011F] Train Service Planning

神仙题。这个银牌不亏。

首先题意需要读明白。我语文和英语都不是很好,翻了翻题解才知道题面在说什么。

首先显然时间可以 \(\bmod k\) 意义下搞。然后只要搞 \(0-n\)\(n-0\) 两辆车。那我们设两个数组 \(p,q\),分别为第一辆和第二辆车在每个站停的时间,并做前缀和 \(suma,sump,sumq\)

那么我们的条件就变成了若干区间不交。即:

\[(sump_{i-1}+suma_{i-1},sump_{i-1}+suma_i)\cap(-sumq_{i-1}-suma_i,-sumq_{i-1}-suma_{i-1})=\emptyset \]

这个负号可能有点奇怪。其实由于一整轮的时间是 \(k\) 的倍数,那么 \(\bmod k\) 意义下就没了,只剩下减掉的前缀和。

解一车方程,得到

\[sump_{i-1}+sumq_{i-1}\in[-2suma_{i-1},-2suma_i] \]

然后 \(sump,sumq\) 递增,因此问题就变成了:有若干区间 \([l_i,r_i]\),初始有一个数 \(x\),初值任意,每次加一个数使得落在区间内,求最少加多少。

考虑解决这个问题。若起点确定,下一次显然走到左端点最优。那么先预处理每个起点出发后一直走左端点的最小距离,这个可以整棵线段树,每次查区间 \([l_i,r_i]\) 的时候查到后面第一个和这个区间无交的区间 \([l_j,r_j]\),用 \(dp_j+dis(l_i,l_j)\) 更新 \(dp_i\),然后覆盖区间。

最后枚举所有的 \(l_i,r_i\) 为起点统计答案就可以了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
int n,mod,cnt,a[200010],b[200010],sum[200010],l[200010],r[200010],lsh[400010];
int tree[800010],dp[200010];
void pushdown(int rt){
    if(tree[rt]){
        tree[lson]=tree[rson]=tree[rt];tree[rt]=0;
    }
}
void update(int rt,int L,int R,int l,int r,int val){
    if(l>r)return;
    if(l<=L&&R<=r){
        tree[rt]=val;return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)update(lson,L,mid,l,r,val);
    if(mid<r)update(rson,mid+1,R,l,r,val);
}
int query(int rt,int l,int r,int pos){
    if(l==r)return tree[rt];
    pushdown(rt);
    int mid=(l+r)>>1;
    if(pos<=mid)return query(lson,l,mid,pos);
    else return query(rson,mid+1,r,pos);
}
int get(int pos){
    int x=query(1,1,cnt,pos);
    if(!x)return 0;
    return dp[x]+(lsh[l[x]]-lsh[pos]+mod)%mod;
}
signed main(){
    scanf("%lld%lld",&n,&mod);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&a[i],&b[i]);
        sum[i]=sum[i-1]+a[i];
        if(b[i]==1&&2*a[i]>mod){
            puts("-1");return 0;
        }
    }
    for(int i=1;i<=n;i++){
        if(b[i]==1){
            l[i]=(mod-2ll*sum[i-1]%mod)%mod;
            r[i]=(mod-2ll*sum[i]%mod)%mod;
        }
        else l[i]=0,r[i]=mod-1;
        lsh[++cnt]=l[i];lsh[++cnt]=r[i];
    }
    sort(lsh+1,lsh+cnt+1);
    cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
    for(int i=n;i>=1;i--){
        l[i]=lower_bound(lsh+1,lsh+cnt+1,l[i])-lsh;
        r[i]=lower_bound(lsh+1,lsh+cnt+1,r[i])-lsh;
        dp[i]=get(l[i]);
        if(l[i]>r[i])update(1,1,cnt,r[i]+1,l[i]-1,i);
        else update(1,1,cnt,1,l[i]-1,i),update(1,1,cnt,r[i]+1,cnt,i);
    }
    int ans=dp[1];
    for(int i=cnt;i>=1;i--)ans=min(ans,get(i));
    printf("%lld\n",ans+2*sum[n]);
    return 0;
}
posted @ 2023-03-16 17:25  gtm1514  阅读(25)  评论(0编辑  收藏  举报