14644:经典**题

严肃的声明

所以说一本通什么时候学会骂人了?

为了做一个文明人,我把那个词换成了**,**就是笨蛋的意思,到时候查题我还是建议查题号,免得理解错了。

一本通是14644,BZOJ是4644,洛谷没找到估计没有。

那首先我在下面说一下题目,那个词我也用**代替。

题目(为了方便统一用一本通的数据)

14644:经典**题


时间限制: 1000 ms         内存限制: 524288 KB
提交数: 10     通过数: 7

【题目描述】

这是一道经典**题,对经典题很熟悉的人也不要激动,希望大家不要**。

考虑一张N个点的带权无向图,点的编号为1到N。 对于图中的任意一个点集(可以为空或者全集),所有恰好有一个端点在这个点集中的边组成的集合被称为割。 一个割的权值被定义为所有在这个割上的边的异或和。

一开始这张图是空图, 现在,考虑给这张无向图不断的加边, 加入每条边之后,你都要求出当前权值最大的割的权值, 注意加入的边永远都不会消失。

【输入】

输入的第一行包括一个数ID表示数据编号, 如第一组数据中的ID = 1。注意样例数据中的ID = 0。

接下来的第一行包括两个整数N,M表示图的点数和总共加的边。

接下来M行,每行三个正整数x,y,w表示在点x和点y之间加入一条权值为w的边。 

注意x和y可能相同,两条不同的边也可能连接了同一对点。

此外, w将以二进制形式从高位向低位给出,比如, 6 = 110(2),因此如果边权为 6,那么w将会是110。

1 ≤ N≤ 500, 1 ≤ M ≤ 1000, 0 ≤ L < 1000, 1 ≤ x,y≤ N

【输出】

输出M行,按顺序输出每一条边加入之后权值最大的割的权值。

同样,你也要以二进制形式输出,形式和输入格式中描述的形式一样。

【输入样例】

0 3
6
1 2 1
1 2 1
3 3 111
1 3 101101
1 2 1011
2 3 111011

【输出样例】

1 0 0
101101
101101
110000

【提示】

前三条边加入之后的答案较为显然,考虑后三条边,加入第六条边之前, 考虑点集{1,2},它对应的割只有第四条边, 因此答案就是第四条边的权值,考虑加入最后一条边以后的情况,此时点集{1,2}对应的割变成了第四条边和第六条边组成的集合,权值也发生了相应的改变。 点集{2}对应的割是第五条边和第六条边组成的集合, 可以证明这就是权值最大的割,权值为1011(2) ⊕ 111011(2) =110000(2)

【来源】

没有写明来源

如果没有把某个词换掉,请在评论区说一下。

解析

        这个问题涉及到图论中一个复杂且特殊的问题——计算随着边的添加,权值最大的割的权值变化。解这类问题通常需要深入理解图的性质及动态规划或者贪心策略与位运算的高级应用,特别是考虑到边的权值是以二进制形式给出,并要求输出也是二进制形式。然后我的思路分五步。

1. 初始化

         由于起始是空图,没有割,所以最初的权值最大的割的权值为0。

2. 维护结构

         需要维护一个数据结构来快速更新和查询当前图的最大割权值。考虑到每次操作只增加一条边,且需要高效查询最大割,可以考虑使用某种形式的势能函数或者利用二进制的特性来优化查询过程。一个可能的思路是动态维护一个“势能”数组,数组的每个元素代表当前割在某个特定二进制位上是否有优势(即是否能贡献最大异或和)。

3. 处理新边

        每加入一条新边(x, y, w)时,实际上是在考虑这条边是否能参与到形成新的最大割中。由于异或运算的特性(即相同位为0,不同位为1时结果为1),新加入的边可能会改变某些割的权值。特别是,如果w在某一位是1,且当前图中不存在其他边能在这一位贡献1(即异或后为1),这条边就很关键,因为它可以改变全局最大割的二进制表示。

4. 更新策略(最重要的一步)

        更新策略应当能够高效反映新边的影响。可能的做法是对于每个点维护一个状态,表示在其关联的割中,哪些位是“确定”的(即无论如何添加边,这些位的值不会改变)。然后,对于新加入的边,检查它是否能与已有边形成新的最大割,这可能需要回顾或重新计算某些点的状态。

5. 输出结果

        根据上述维护的信息,每次加入边后输出当前权值最大的割的二进制表示。

代码(C++版,其他编程语言可以自己把这个代码修改)

#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<bitset>
using namespace std;
int n,m,tag[505];
char s[1005];
vector<bitset<1005>>ve[4005];
vector<int>bin[4005];
bitset<1005>las[1005],temp;
inline int read(){
    int f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return f*w;
}
void update(int i,int l,int r,int L,int R,int p){
    if(L<=l&&r<=R){ve[i].push_back(las[p]);return;}
    int mid=l+r>>1;
    if(L<=mid)update(i<<1,l,mid,L,R,p);
    if(R>mid)update(i<<1|1,mid+1,r,L,R,p);
}
void insert(int x,int y){
    for(int i=1;i<=1000;i++)if(ve[x][y][i])
        if(!las[i][i]){las[i]=ve[x][y],bin[x].push_back(i);break;}
        else ve[x][y]^=las[i];
}
void query(){
    temp.reset();
    for(int i=1;i<=1000;i++)if(!temp[i])temp^=las[i];
    int i=1;
    for(;i<=1000&&!temp[i];i++);
    if(i==1001)puts("0");
    else{
        for(;i<=1000;i++)printf("%d",(temp[i]==1));
        putchar(10);
    }
}
void dfs(int i,int l,int r){
    for(int j=0;j<ve[i].size();j++)insert(i,j);
    if(l==r)query();
    else{
        int mid=l+r>>1;
        dfs(i<<1,l,mid),dfs(i<<1|1,mid+1,r);
    }
    for(int j=0;j<bin[i].size();j++)las[bin[i][j]].reset();
}
int main(){
    read(),n=read(),m=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read(),len;
        scanf("%s",s+1),len=strlen(s+1),temp.reset();
        if(x==y)continue;
        for(int j=1;j<=len;j++)temp[1000-len+j]=s[j]-'0';
        if(tag[x])update(1,1,m,tag[x],i-1,x);
        if(tag[y])update(1,1,m,tag[y],i-1,y);
        tag[x]=tag[y]=i,las[x]^=temp,las[y]^=temp;
    }
    for(int i=1;i<=n;i++)if(tag[i]<=m&&tag[i])update(1,1,m,tag[i],m,i),las[i].reset();
    dfs(1,1,m);
    return 0;
}

总结

        我觉得这道题关键在于构建并维护一个高效的数据结构来反映边的异或关系,并能根据新边的加入快速调整策略以求解每个阶段的最大割权值。

难题(求解决)

一本通里面有一道题太难了,我用小号试了大约30多次最高也才5分。如果有神犇做出来,拜托在CSDN里私信我,告诉我怎么做,实在感谢!链接

  • 41
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值