做题纪要

Alice and Recoloring 1#

有一个很牛逼的转化,考虑一个点 i,j 是否被以此为端点进行区间覆盖,只需考虑 (i+1,j)(i,j+1)(i+1,j+1) 是为 B 的个数,如果个数为偶数,则此点不许操作,否则则需操作。设原序列 ai,j=[si,j=B] , ci,j=ai+1,jai,j+1ai+1,j+1ai,j,所以将 a 清空其实就是将 c 清空,而且还有一个比较好的地方就是进行操作一其实就是修改一个点,而进行操作四就是修改四个点形如:(x+1,y+1) (x,y)(x,m)(n,y)(n,m),再考虑,操作 2,3就是在搞笑,完全可以被两次操作一代替,还有两次操作四可以被六次操作一代替,所以操作四最多进行一次,然后直接枚举那一次选在哪里就可以。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=505;
char s[N];
int c[N][N];
int a[N][N];
int sum[N][N];
signed main(){
    int n,m;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        for(int j=1;j<=m;j++){
            if(s[j]=='B') c[i][j]=1;
            else c[i][j]=0;
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            a[i][j]=(c[i][j]^c[i+1][j]^c[i][j+1]^c[i+1][j+1]);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
            sum[i][j]+=a[i][j]; 
        }
    }
    int ans=sum[n][m];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int w;
            w=a[n][m]+a[i-1][j-1]+a[n][j-1]+a[i-1][m];
            ans=min(ans,3+sum[n][m]-w+4-w);
        }
    }
    printf("%lld",ans);
}

Alice and Recoloring 2#

转换和上道题一样,唯一不同的就是两次操作四比六次会比操作一优,所以操作四可以操作很多次,但还有一些其他性质需要挖掘:

  • 若两次四操作为 (x,y1)(x,y2) 则会改变四个 c 值,则可以用四次操作一代替,纵坐标相等横坐标不同同理,所以四操作不会出现在同一行同一列。

  • 四操作假如 (x,m),(x,y),(n,y) 不全为一,则会产生一次负贡献,需要用一代价的操作一代替(不可能是操作四,由👆可知),所以仅反转三个格子总代价为 2+1=3,可以用一代替,所以只有那三个格子均为一,才会进行操作四。

所以肯定是合法的操作四越多越好,为了求这个,也就是二分图最大匹配,考虑 Dinic ,左侧 n1 个点,右侧 m1 个点,对于合法四操作连一条边权为 1 的边,跑最大流就可以力。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=600;
const int M=360000;
const int inf=0x3f3f3f3f3f;
char s[N];
int c[N][N];
int a[N][N];
int st,ed;
int head[M*2],ver[M*2],nex[M*2],edge[M*2],tot=1;
void add(int x,int y,int v){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot,edge[tot]=v;
    ver[++tot]=x,nex[tot]=head[y],head[y]=tot,edge[tot]=0;
}
int dep[M],cur[M];
int bfs(){
    memset(dep,0,sizeof(dep));
    dep[st]=1;
    queue<int> q;
    q.push(st);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=nex[i]){
            int y=ver[i];
            if(dep[y] || edge[i]==0) continue;
            dep[y]=dep[x]+1;
            q.push(y); 
        }
    }
    if(dep[ed]) return 1;
    else return 0;
}
int dfs(int x,int flow){
    if(x==ed) return flow;
    for(int i=cur[x];i;i=nex[i]){
        cur[x]=i;
        int y=ver[i];
        if(dep[y]==dep[x]+1 && edge[i]){
            int d=dfs(y,min(flow,edge[i]));
            if(d>0){
                edge[i]-=d;
                edge[i^1]+=d;
                return d;
            }
        }
    }
    return 0;
}
int Dinic(){
    int ans=0;
    while(bfs()){
        for(int i=1;i<=ed;i++)
            cur[i]=head[i];
        while(int d=dfs(st,inf)){
            ans+=d;
        }
    }
    return ans;
}
signed main(){
    int n,m;
    scanf("%lld%lld",&n,&m);
    int sum=0;
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        for(int j=1;j<=m;j++){
            if(s[j]=='B') c[i][j]=1;
            else c[i][j]=0;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[i][j]=(c[i][j]^c[i+1][j]^c[i][j+1]^c[i+1][j+1]);
            if(a[i][j]) sum++;
        }
    }
    st=n+m+1,ed=st+1;
    for(int i=1;i<n;i++) add(st,i,1);
    for(int i=1;i<m;i++) add(n+i,ed,1);
    for(int i=1;i<n;i++){
        for(int j=1;j<m;j++){
            if(a[i][j] && a[i][m] && a[n][j]){
                add(i,n+j,1);
            }
        }
    }
    int cnt=Dinic();
    int ans=0;
    ans+=cnt*2;
    int w=sum-cnt*3;
    if(a[n][m] && cnt%2) ans--;
    if(!a[n][m] && cnt%2) ans++;
    printf("%lld",ans);
    return 0;
}

[ABC324F] Beautiful Path#

题干要求 bc 最大,所以可以二分一个最大值 k,然后就变成了 bck,移过去变为 bc×k0,然后边权就变为了 bk×c,然后拓扑跑最大路,看是否大于等于0。

点击查看代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=2*1e5+10;
const long double eps=1e-15;
int head[N*2],ver[N*2],nex[N*2],tot=0;
long double edge[N*2];
void add(int x,int y,long double v){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot,edge[tot]=v;
}
struct asd{
    int x,y;
    long double b,c;
}a[N];
int n,m;
long double dp[N];
bool dij(int p){
    for(int i=1;i<=n;i++) dp[i]=-(1<<25);
    dp[p]=0;
    for(int x=1;x<n;x++){
        for(int i=head[x];i;i=nex[i]){
            int y=ver[i];
            dp[y]=max(dp[y],dp[x]+edge[i]);
        }
    }
    return dp[n]>=0;
}

int check(long double k){
    tot=0;
    memset(head,0,sizeof(head));
    for(int i=1;i<=m;i++){
        int x=a[i].x,y=a[i].y;
        long double w=a[i].b-k*a[i].c;
        add(x,y,w);
    }
    return dij(1);
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++) scanf("%lld%lld%Lf%Lf",&a[i].x,&a[i].y,&a[i].b,&a[i].c);
    long double l=0,r=21e9;
    while(r-l>eps){
        long double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%0.20Lf",l);

}

「KDOI-06-S」树上异或#

树形 dp 确实很显然,按位计算也很显然,然后就不会了,lhx 是强的,这也很显然,可以把这个分成两部分相乘,一部分是完整的块的乘积,另一部分是还未乘上贡献的方案数,转移的时候一个一个合并到一起,所以设 fi,j,0/1 表示以 i 为根的子树形成的块在二进制下第 j 位的且不完整块有偶数/奇数个 j 的贡献,所以

fx,j,0fx,j,0×fy,j,0+fx,j,1×fy,j,1

fx,j,1fx,j,0×fy,j,1+fx,j,1×fy,j,0

然后考虑断边的操作 ,其实就是将当前不完整块个数为奇数的贡献计算出来,然后将其加到全部的 fx,j,0 上即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5*1e5+5;
const int mod=998244353;
int f[N][65][3];
long long a[N];
int head[N*2],ver[N*2],nex[N*2],tot=0;
void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int dfs(int x,int fa){
    for(int i=1;i<=61;i++){
        if(a[x]&(1ll<<(i-1))) f[x][i][1]=1;
        else f[x][i][0]=1;
    }
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fa) continue;
        dfs(y,x);
        for(int j=1;j<=61;j++){
            int f0=f[x][j][0],f1=f[x][j][1];
            f[x][j][0]=1ll*(1ll*f0*f[y][j][0]%mod+1ll*f1*f[y][j][1]%mod)%mod;
            f[x][j][1]=1ll*(1ll*f0*f[y][j][1]%mod+1ll*f1*f[y][j][0]%mod)%mod;    
        }
    }
    long long sum=0;
    for(int i=1;i<=61;i++) sum=1ll*(1ll*sum+1ll*(1ll<<(i-1))%mod*f[x][i][1]%mod)%mod;
    for(int i=1;i<=61;i++) f[x][i][0]=1ll*(1ll*f[x][i][0]+1ll*sum)%mod;
    return sum;
}
signed main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=2;i<=n;i++){
        int x;
        scanf("%d",&x);
        add(i,x),add(x,i);
    }
    long long ans=dfs(1,0);
    printf("%lld",ans);
}

作者:bloss

出处:https://www.cnblogs.com/jinjiaqioi/p/17762864.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   _bloss  阅读(38)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu