AGC006

一些痛苦的回忆。

然后发现我现在连VP都懒得点,顶多算板刷。

感觉现在的一个效果是黑题AC数量明显上升。(或者说找到了一些水黑)

[AGC006A] Prefix and Suffix

普及-。闲着没事写了个哈希。

const int mod=1000000007,prm=131;
unsigned long long hss[110],hst[110],p[110];
int n;
char s[110],t[110];
unsigned long long gethss(int l,int r){
    return hss[r]-hss[l-1]*p[r-l+1];
}
unsigned long long gethst(int l,int r){
    return hst[r]-hst[l-1]*p[r-l+1];
}
int main(){
    scanf("%d%s%s",&n,s+1,t+1);p[0]=1;
    for(int i=1;i<=n;i++){
        hss[i]=hss[i-1]*prm+s[i];
        hst[i]=hst[i-1]*prm+t[i];
        p[i]=p[i-1]*prm;
    }
    for(int i=n;i>=1;i--){
        if(gethst(1,i)==gethss(n-i+1,n)){
            printf("%d\n",(n<<1)-i);
            return 0;
        }
    }
    printf("%d\n",n<<1);
    return 0;
}

[AGC006B] Median Pyramid Easy

我们只需要让第 \(n-1\) 层中间的两个数是 \(x\) 那这个 \(x\) 的位置就永远不会变。所以把 \(x-1,x,x+1\) 顺序放中间三个位置就行了。特判 \(1\)\(2n-1\) 无解。

int n,x,a[200010];
bool v[200010];
int main(){
    scanf("%d%d",&n,&x);
    if(x==1||x==2*n-1){
        puts("No");return 0;
    }
    puts("Yes");
    a[n]=x;a[n-1]=x-1;a[n+1]=x+1;
    v[x]=v[x-1]=v[x+1]=true;
    int pos=1;
    for(int i=1;i<=2*n-1;i++){
        if(i==n||i==n-1||i==n+1)continue;
        while(v[pos])pos++;
        v[pos]=true;a[i]=pos;
    }
    for(int i=1;i<=2*n-1;i++)printf("%d\n",a[i]);
    return 0;
}

[AGC006C] Rabbit Exercise

写了个题解打算水社贡。

一次操作相当于把 \(a_i\) 变成 \(a_{i-1}+a_{i+1}-a_i\) ,就是交换差分。把差分顺序看成一个排列,快速幂就行了。

开long long。

#define int long long
int n,m,x[100010],a[100010],p[100010],ans[100010],tmp[100010];
long long k;
void qpow(int a[],long long b){
    while(b){
        if(b&1){
            for(int i=1;i<=n;i++)tmp[i]=ans[p[i]];
            for(int i=1;i<=n;i++)ans[i]=tmp[i];
        }
        for(int i=1;i<=n;i++)tmp[i]=p[p[i]];
        for(int i=1;i<=n;i++)p[i]=tmp[i];
        b>>=1;
    }
}
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&x[i]),p[i]=ans[i]=i;
    scanf("%lld%lld",&m,&k);
    for(int i=1;i<=m;i++){
        scanf("%lld",&a[i]);
        swap(p[a[i]],p[a[i]+1]);
    }
    qpow(p,k);
    for(int i=1;i<=n;i++)a[i]=x[i]-x[i-1];
    for(int i=1;i<=n;i++)tmp[i]=a[ans[i]];
    for(int i=1;i<=n;i++)a[i]=tmp[i];
    for(int i=1;i<=n;i++)x[i]=x[i-1]+a[i];
    for(int i=1;i<=n;i++)printf("%lld.0\n",x[i]);
    return 0;
}

[AGC006D] Median Pyramid Hard

当时没有做出来。那场模拟赛一车 200 然后我勉强 100。

一个很妙的二分答案。二分第一层的数字,把大于等于的赋成 \(1\),小于的变成 \(0\)。然后会发现和中间距离最近的三个相同字符的连续段会占领最高点。特判最下层 01 交替的情况。

int n,a[200010],b[200010];
bool check(int mid){
    for(int i=1;i<=2*n-1;i++)b[i]=(a[i]>=mid?1:0);
    for(int i=0;i<n-1;i++){
        if(b[n+i]==b[n+i+1])return b[n+i];
        if(b[n-i]==b[n-i-1])return b[n-i];
    }
    return b[1];
}
int main(){
        scanf("%d",&n);
        for(int i=1;i<=2*n-1;i++)scanf("%d",&a[i]);
        int l=1,r=2*n-1;
        while(l<r){
            int mid=(l+r)>>1;
            if(check(mid))l=mid+1;
            else r=mid;
        }
        printf("%d\n",l-1);
    return 0;
}

[AGC006E] Rotate 3x3

怎么又是黑排序题。

首先判掉一些 sb 情况。

然后我们这个操作可以把任意隔着一列的两列颠倒一下。那么只要奇数位置和偶数位置上排好序之后都满足颠倒的位置数量是偶数就行了。

考虑交换两列的影响。这两列会被颠倒一下,不影响奇偶性。如果操作的是奇数列,那么中间的偶数列会被颠倒,反之中间的奇数列会被颠倒。也就是把一个元素排好在和它奇偶性相反的数列的贡献是它产生的逆序对数。爆算就行了。

我怎么写这么长。

int n,a[100010][3],lsh[100010];
int lowbit(int x){
    return x&-x;
}
struct BIT{
    int c[100010];
    void update(int x,int val){
        while(x<=n)c[x]+=val,x+=lowbit(x);
    }
    int query(int x){
        int sum=0;
        while(x)sum+=c[x],x-=lowbit(x);
        return sum;
    }
}c1,c2;
int main(){
    scanf("%d",&n);
    for(int j=0;j<3;j++){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i][j]);
            int ret=(a[i][j]-1)/3+1;
            if((ret&1)!=(i&1)){
                puts("No");return 0;
            }
        }
    }
    for(int i=1;i<=n;i++){
        int ret=a[i][1];
        lsh[i]=ret;
        if(ret%3!=2){
            puts("No");return 0;
        }
        if(a[i][0]==ret-1&&a[i][2]==ret+1)continue;
        if(a[i][0]==ret+1&&a[i][2]==ret-1)continue;
        puts("No");return 0;
    }
    sort(lsh+1,lsh+n+1);
    int cnt1=0,cnt2=0;
    for(int i=1;i<=n;i++){
        int ret=(a[i][0]>a[i][1])?-1:1;
        a[i][1]=lower_bound(lsh+1,lsh+n+1,a[i][1])-lsh;
        if(i&1){
            if(ret==-1)cnt1^=1;
            c1.update(a[i][1],1);
            int tmp=((i+1)>>1)-c1.query(a[i][1]);
            if(tmp&1)cnt2^=1;
        }
        else{
            if(ret==-1)cnt2^=1;
            c2.update(a[i][1],1);
            int tmp=((i+1)>>1)-c2.query(a[i][1]);
            if(tmp&1)cnt1^=1;
        }
    }
    if(cnt1||cnt2)puts("No");
    else puts("Yes");
}

[AGC006F] Blackout

终于跳出第 30 页了。

首先一个显然的套路是对于点 \((x,y)\)\(x\)\(y\) 连边。然后手模了几组样例,感觉和三元环有点关系,就是每次找到一条三个元素的链就由尾到头连个边,最后数一遍多少边。看了一眼题解发现是染色,那我是 sb。

然后继续手推。发现可以对于每个联通块三染色 \(1\rightarrow 2\rightarrow 3\rightarrow 1\) 这样。然后分类讨论:

  1. 不能三染色:这个手模几组发现最后整个连通块内部都可以互相连边,贡献是 \(n^2\)
  2. 可以三染色:分两种。一种是三种颜色没有染完,那显然没法连边,贡献就是原来的边数。另一种是三种颜色染完了,那此时的贡献就是三种颜色两两乘起来的加和,分别对应三类边。

然后就没了。感觉图的染色还是不容易想到。

一个小 trick 是建边的时候建边权 \(-1\) 的反向边,方便处理。

#define int long long
struct node{
    int v,w,next;
}edge[200010];
int t,n,m,pnt,eg,head[100010],c[100010],cnt[5];
long long ans;
bool jud;
void add(int u,int v,int w){
    edge[++t].v=v;edge[t].w=w;
    edge[t].next=head[u];head[u]=t;
}
void dfs(int x,int col){
    c[x]=col;pnt++;cnt[col]++;
    for(int i=head[x];i;i=edge[i].next){
        eg++;
        int co=c[x]%3+edge[i].w;
        if(co<=0)co+=3;
        if(c[edge[i].v]){
            if(c[edge[i].v]!=co)jud=false;
        }
        else dfs(edge[i].v,co);
    }
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;scanf("%lld%lld",&u,&v);
        add(u,v,1);add(v,u,-1);
    }
    for(int i=1;i<=n;i++)if(!c[i]){
        pnt=eg=0;cnt[1]=cnt[2]=cnt[3]=0;
        jud=true;
        dfs(i,1);
        if(!jud)ans+=1ll*pnt*pnt;
        else{
            if(!cnt[1]||!cnt[2]||!cnt[3])ans+=eg>>1;
            else ans+=1ll*cnt[1]*cnt[2]+1ll*cnt[2]*cnt[3]+1ll*cnt[3]*cnt[1];
        }
    }
    printf("%lld\n",ans);
    return 0;
}

完了。

posted @ 2022-11-15 20:10  gtm1514  阅读(30)  评论(0编辑  收藏  举报