[离散化][玄学]终极武器

题目描述

经过一番周折,精英队伍的队员们终于来到了关押applepi的牢狱面前。心中神一般的领袖applepi就在眼前,队员们都不由自主地跪烂膝盖……不过令他们沮丧的是,牢狱的大锁没有钥匙孔,黑魔法师Vani根本就没有指望它再被打开。幸好队员们携带了新研制的终极武器——k型氙激光器(Xenon Laser - k,代号XLk),可以用来破拆这把锁。不过作为一道终极武器,它的启用规则异常严格。
  Xenon Laser - k上共有N个波段能够发射激光,每个波段可以用一个闭区间[ai,bi]来表示,其中ai,bi为正整数,b[i-1]<ai≤bi。对于两个数字p和q,如果对于这N个波段内的任意一个整数num,把它在十进制表示下的后k位中某一位上的p换成q(或者q换成p),都满足得到的整数仍然在这N个波段内,那么称在该激光器中,数字p和q是k等价的。我们称两两之间k等价的数字组成一个k等价类。
  激光器附带了9个发射匣,代表1~9这9个数字。只有把同一个等价类的数字对应的发射匣安置在一排上,Xenon Laser - k才能够启动。给定个波段,现在就请你求出1~9这9个数字分成了哪些等价类,并且每行输出一个等价类。
  本题描述比较抽象,请参考样例解释。

Input
第一行两个整数N,k。

接下来N行每行两个整数ai,bi。ai,bi为正整数,满足b[i-1]

分析

以下部分源自他人题解
读入所有区间之后,转化为前闭后开形式即 [L,R),存入数组……然后我们拿出其中的一个区间来考虑。。。
单独对于这个区间,我们考虑第 k 位上的数字变化。如果我们知道比第 k 位低的位上都是都是什么数字的话,显然可以很容易地判断出这一位上的数字 1~9 可以分成哪两组等价类……
比如区间[2012,6278),考虑十位。如果规定各位数字是 0,那么[2020,6280)中的数在区间内,剩下的在区间外,即十位上[2,8)是一个等价类,其余数字是另一个。如果规定个位数字是 2,那么十位上是[1,8),如果个位数字是 8,那么十位上是[1,7)。
可以发现最初第 k 位上的数字的其中一组是[L/10^k %10 +1, R/10^k %10 +1),当后面的位经过 L%10^k 这个数的时候区间左端减一,经过 R%10^k 这个数的时候区间右端减一。
因此我们可以依次处理每一位,对于每一位上,把读入的 n 个区间中,所有这样引起该位数字区间变化的数值记录下来,排序离散化之后再依次处理。可以发现引起变化的位置只可能是这 n 个区间的左右端点,并且变化规律就是上面一段所说的。
大概梳理一下整个算法过程:
初始化:建一个 9*9 的完全图的邻接矩阵。
特殊处理个位情况。
主要环节:1.枚举每一位,设当前枚举到第 k 位。
2.按照上面所述的方法计算引起变化的位置,排序,离散化,离散后的每个位置开一个 1*9的数组记录哪些数字在数字区间内,哪些在外面。
3.首先令后 k-1 位都是 0,计算各个数字区间(1*9 的数组)的初始值。
4.依次循环每个变化位置,对应的 1*9 的数组里进行加减。
5.每次变化后把对应的 1*9 数组的信息反映到 9*9 的矩阵里,不同集合内的边断掉。
输出答案。
以下是我的细节部分:
首先照着上面算是没错的,然后我搞了一个判别值(估计是哈希的作用):
对于第w位,判别值为: nubmermod10w1 n u b m e r mod 10 w − 1 通过对这个值的排序达到离散化的效果
然后我用了一个叫array的结构体储存对于第二步的1*9数组和离散化数组的开头结尾
然后一波排序,离散化,修改值,就可以用dfs输出了
貌似我和JZ的yangle大佬撞思路了,如有雷同,纯属偶然

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
#define rep(i,a,b) for (i=a;i<=b;i++)
typedef long long ll;
const int N=10001;
using namespace std;
bool d[10][10],f[4*N][10],v[N];
ll l[N],r[N],pow[20],pc[2];
int n,k,cnt;
struct ptinarray {
    ll num,bck;
    bool flg;
    void read(ll n1,ll b1,bool f1) {
        num=n1;bck=b1;flg=f1;
    }
}p[4*N];
int pcnt;
bool Cmp(ptinarray a,ptinarray b) {return a.bck<b.bck;}
struct array {
    int h,t;
    bool f[10];
    void sortpt() {
        sort(p+h+1,p+t+1,Cmp);
    }
    void Paint_line(int l,int r) {
        int i;
        rep(i,l,r) f[i]=1;
    }
}a[4*N];

void Init() {
    int i,j;
    scanf("%d%d",&n,&k);
    pow[0]=1;
    rep(i,1,19) pow[i]=pow[i-1]*10;
    memset(d,1,sizeof d);
    rep(i,1,n) {
        scanf("%lld%lld",&l[i],&r[i]);
        if (r[i]==pow[18]) {
            r[i]--;
            rep(j,2,9) d[1][j]=d[j][1]=0;
        }
        if (k==19)
        k--;
    }
}

void Update(bool *c) {
    int ca[10],cb[10],i,j;
    memset(ca,0,sizeof ca);memset(cb,0,sizeof cb);
    rep(i,1,9) if (c[i]) ca[++ca[0]]=i; else cb[++cb[0]]=i;
    rep(i,1,ca[0]) rep(j,1,cb[0]) d[ca[i]][cb[j]]=d[cb[j]][ca[i]]=0;
}

void Unit_push(ll x,ll l,ll r) {
    int i;
    if (pc[cnt%2]!=x) pc[++cnt%2]=x;
    rep(i,l,r) f[cnt][i]=1;
}

void Unit() {
    int i;
    cnt=1;
    rep(i,1,n) {
        ll dl=l[i]/pow[1],dr=r[i]/pow[1],l1=l[i]%pow[1],r1=r[i]%pow[1];
        if (dl==dr) Unit_push(dl,l1,r1);
        else {
            if (l1>1) Unit_push(dl,l1,9);
            if (r1&&r1<9) Unit_push(dr,1,r1);
        }
    }
    rep(i,1,cnt) Update(f[i]);
}

void Digit_push(ll x,ll l,ll r,int w) {
    int i;
    if (pc[cnt%2]!=x) {
        a[cnt].t=pcnt;
        pc[++cnt%2]=x;
        a[cnt].h=pcnt;
    }
    a[cnt].Paint_line(l/pow[w-1]+1,(r-1)/pow[w-1]);
    if (l!=pow[w]) p[++pcnt].read(l/pow[w-1],l%pow[w-1],0);
    if (r!=pow[w]) p[++pcnt].read(r/pow[w-1],r%pow[w-1],1);
}

void Digit(int w) {
    int i,j;
    cnt=1;pcnt=0;pc[0]=pc[1]=0;
    rep(i,1,n) {
        ll dl=l[i]/pow[w],dr=r[i]/pow[w],l1=l[i]%pow[w],r1=r[i]%pow[w];
        r1++;
        if (dl==dr) Digit_push(dl,l1,r1,w);
        else {
            if (l1>1) Digit_push(dl,l1,pow[w],w);
            if (r1&&r1<pow[w]-1) Digit_push(dr,pow[w-1],r1,w);
        }
    }
    a[cnt].t=pcnt;
    rep(i,1,cnt) {
        a[i].sortpt();
        rep(j,a[i].h+1,a[i].t) {
            a[i].f[p[j].num]=!p[j].flg;
            if (p[j].bck!=p[j+1].bck) Update(a[i].f);
        }
    }
}

void Dfs(int u) {
    int i;
    v[u]=1;
    printf("%d",u);
    rep(i,1,9)
    if (d[u][i]&&!v[i]) Dfs(i);
}

void Solve() {
    int i;
    Unit();
    rep(i,2,k) {
        memset(p,0,sizeof p);memset(a,0,sizeof a);
        Digit(i);
    }
    rep(i,1,9)
    if (!v[i]) {
        Dfs(i);
        printf("\n");
    }
}

int main() {
    Init();
    Solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值