GDOI2016模拟8.8处理器

本文讨论了微型处理器的寄存器操作,特别是旋转和异或操作,并利用并查集解决相关问题。通过逐位分析和并查集算法,实现了解决方案的高效求解。

题目
你有一个微型处理器,处理器有N个寄存器,编号为1到N,每个寄存器用二进制存储一个32位无符号整数(取值范围为0到2^32-1)。存储器能执行以下操作:

指令 描述 Example
1 K M 把寄存器K里面的数字向右旋转M位,再把结果写进寄存器K。 00000000000000000010001111111011

→ (M = 1010) → 11111110110000000000000000001000

(十进制下: 9211 → (M = 10) → 4 273 995 784)
2 K L 把寄存器K和L中的值进行异或运算,结果输出到系统总线中。 00000000000000000000001111000111

XOR 00000000000001111100000000000111

= 00000000000001111100001111000000

(十进制下: 967 XOR 507 911 = 508 864)

如果有多种解,输出字典序最小的解(如果两组解的前K-1个寄存器值相同,第K个寄存器不同,则第K个寄存器的值较小的解字典序更小)。

这题我们可以将每个寄存器的每一位抽出来考虑相互的关系,对于旋转操作,我们可以维护起始指针位置来还原回去后一一对应考虑。

然后用并查集做。
由于会有后效性,而且不能保证字典序最小,我们不能通过一开始决定x属于1或属于0,但可以通过维护x与其并查集顶部位的数是否相同来做。

对于x和y间的关系,若x和y在同一个并查集里,那么若x与y不同,而x与并查集顶部相同,y与并查集顶部也相同,则输出nema(还有x与y相同,同理可推)

若x和y在不同并查集里,那么我们通过x与y的关系,以及x与其并查集顶部的关系,和y与其并查集顶部的关系,推得将两个并查集顶部合并时连边的权值,若为1,则说明两个顶部不同,为0则相同。(动手推一下)

这些权值在getfather时迭代加起来,得到x与顶部是否相同。(奇数为不同,偶数为相同)

最后求答案,从高位枚举,看一下其所在并查集是否已确定,确定了的话,直接按限制得出,反之为0,然后按限制强制将顶部赋值。

贴代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100001
using namespace std;
int n,m;
int fa[N*32][2],s[N];
long long help[32];
bool bz[N*32],ans[N*32];
bool p;
void pre(){
    help[0]=1;
    for (int i=1;i<32;i++)
        help[i]=help[i-1]+help[i-1];
}
int get(int x){
    int y;
    if (fa[x][0]==x)return x;
    y=fa[x][0];
    fa[x][0]=get(fa[x][0]);
    fa[x][1]=(fa[x][1]+fa[y][1])&1;
    return fa[x][0];
}
void init(){
    static int x,y,z;
    static long long v;
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=32;j++)
            fa[(i-1)*32+j][0]=(i-1)*32+j;
    p=1;
    for (int i=1;i<=m;i++){
        scanf("%d %d %d",&x,&y,&z);
        if (x==1)
            s[y]=(s[y]+z)%32;
        else{
            scanf("%lld",&v);
            for (int i=0;i<32;i++){
                static bool e1,e2,e3;
                static int xx,yy;
                e1=v&help[i];
                xx=(y-1)*32+s[y]+1;
                yy=(z-1)*32+s[z]+1;
                get(xx);
                get(yy);
                e2=(fa[xx][1]!=fa[yy][1]);
                e3=(fa[xx][0]==fa[yy][0]);
                if (e3&&e2!=e1)p=0;
                else
                    if (!e3){
                        fa[fa[xx][0]][1]=(e2!=e1);
                        fa[fa[xx][0]][0]=fa[yy][0];
                    }
                s[y]=(s[y]+1)%32;
                s[z]=(s[z]+1)%32;
            }
        }   
    }
}
void write(){
    static int x;
    static long long y;
    if (!p)printf("-1");
    else
    for (int i=1;i<=n;i++){
        y=0;
        for (int j=32;j;j--){
            x=(i-1)*32+j;
            if (!bz[x]){
                get(x);
                if (bz[fa[x][0]])
                    ans[x]=(ans[fa[x][0]]^fa[x][1]);
                else
                    bz[fa[x][0]]=1,ans[fa[x][0]]=fa[x][1];
                bz[x]=1;
            }
            y+=ans[x]*help[j-1];
        }
        printf("%lld ",y);
    }
}
int main(){
    pre();
    init();
    write();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值