Jzoj P3387 终极武器___枚举+dfs

题目大意:

X e n o n L a s e r k 上共有 N 个波段能够发射激光,每个波段可以用一个闭区间 [ a i b i ] 来表示,其中 a i b i 为正整数, b [ i 1 ] a i b i 。对于两个数字 p q ,如果对于这 N 个波段内的任意一个整数 n u m ,把它在十进制表示下的后 k 位中某一位上的 p 换成 q (或者 p 换成 p ),都满足得到的整数仍然在这 N 个波段内,那么称在该激光器中,数字 p q k 等价的。我们称两两之间 k 等价的数字组成一个k等价类。
激光器附带了 9 个发射匣,代表 1 ~ 9 9 个数字。只有把同一个等价类的数字对应的发射匣安置在一排上,Xenon Laser - k才能够启动。给定个波段,现在就请你求出 1 ~ 9 9 个数字分成了哪些等价类。

1 <= n <= 10000 1 <= k <= 19
1 <= a i <= b i <= 10 18

分析:

i = 1 N ( y [ i ] x [ i ] ) < 10 7 ,可以直接枚举合法的数
将它们每一位数都截出来,将每一位上出现过什么都 + 1
然后枚举 i , j
判断是否是等价类,
枚举每一位上,如果全部位上出现的次数都相等的话
两个数字就是等类的

i = 1 N ( y [ i ] x [ i ] ) > 10 7
我们可以从一个为被访问过的数,往后拓展,再判断两者是否合法
如何判断合法
①对于 y [ i ] x [ i ] < 10 5
枚举一个 j 0 <= j <= y [ i ] x [ i ]
t = x [ i ] + j
如果现在是判断 a , b 是否等价类
从后往前将 t 每一位截出来
如果 t == a ,那么使 p = w a i + b i (i就是当前从后往前截到哪一位)
然后再所有x[i],y[i]的区间里跑,如果w在一个区间内,就是等类的
t = y [ i ] j ,然后同上
②对于 ( y [ i ] x [ i ] ) > 10 5
那么 j 枚举 0 <= j <= 10000 ,操作同上

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define fo(i, a, b) for (i = a; i <= b; i++)
#define N 10005
#define M 10

using namespace std;

typedef long long ll;

ll a[M][M], mul[25], cnt[M], vis[M], x[N], y[N];
int f[25][15], n, k;

void read(ll &xx)
{
    ll f=1;xx=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){xx=xx*10+s-'0';s=getchar();}
    xx*=f;
}

void Jud(ll x) {
    ll y = x; 
    int len = 1;
    for (; y; y /= 10) {
        if (len > k) break;
        ++f[len][y%10], ++len;
    }
}

bool Flag(int a, int b) {
    int i;
    fo(i, 1, k)
        if (f[i][a] != f[i][b]) return 0;
    return 1;
}

bool Get_inout(ll a) {
    int i; 
    fo(i, 1, n) if (a >= x[i] && a <= y[i]) return 1;
    return 0;
}

bool pan(ll x, ll a, ll b) {
    ll y = x, len = 1, p = 1;
    for (; y; y /= 10) {
         if (len > k) break;
         if (y%10 == a) 
             if (!Get_inout(x-a*mul[len-1]+b*mul[len-1])) return 0;
         ++len;
    }
    return 1;
}

bool Check(ll a, ll b) {
    int i, j;
    fo(i, 1, n) 
        if (y[i] - x[i] > 10000) {
            fo(j, 0, 9999)
                if (!pan(x[i]+j, a, b) || !pan(x[i]+j, b, a) ||
                    !pan(y[i]-j, a, b) || !pan(y[i]-j, b, a)) return 0;
        } else {
            fo(j, 0, y[i]-x[i]) {
                if (j <= 10 || (x[i]+j)%10 == 0) {
                    if (!pan(x[i]+j, a, b) || !pan(x[i]+j, b, a) ||
                        !pan(y[i]-j, a, b) || !pan(y[i]-j, b, a)) return 0;
                } else {
                    ll w=x[i]+j;
                    if (w%10 == a) {
                        if (!Get_inout(w-a+b)) return 0;
                    } else  if (w%10 == b) {
                                if (!Get_inout(w+a-b)) return 0;
                            }
                }
            }
         }
    return 1;
}

void dfs(ll x, ll y) {
    if (y > 9) return;
    if (Check(x, y)) a[x][++cnt[x]] = y, vis[y] = 1;
    dfs(x, y+1);
}

int main() {
    int i;
    scanf("%d %d", &n, &k);
    ll sum = 0;
    fo(i, 1, n) read(x[i]), read(y[i]), sum += (y[i] - x[i]);
    mul[0] = 1;
    fo(i, 1, 20) mul[i] = mul[i-1]*10;
    if (sum < 1e7) {
        ll j;
        fo(i, 1, n) 
            fo(j, x[i], y[i]) Jud(j);
        fo(i, 1, 9)
            if (!vis[i]) {
                printf("%d", i);
                fo(j, i+1, 9) 
                    if (Flag(i,j)) vis[j] = 1, printf("%d",j);
                printf("\n");
            }
    } else {
            int j;
            fo(i, 1, 9)
                if (!vis[i]) a[i][++cnt[i]] = i, vis[i] = 1, dfs(i, i+1);
            fo(i, 1, 9)
                if (cnt[i]) {
                    fo(j, 1, cnt[i]) printf("%d", a[i][j]); printf("\n");
                }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/gx_man_vip/article/details/81044142
今日推荐