某岛

… : "…アッカリ~ン . .. . " .. .
April 12, 2012

SRM 540

Brief description:

DIV 1 950. ProductQuery:
一个待定的长度为 n 的模 10 数组,一组询问 (l, r, o),表示从第 l 位连乘到第 r 位的结果是 o .. .
要求给定 m 组询问之后还原此数组的可能组数。
(n ≤ 100, m ≤ 50 )

Analysis:

询问之间存在互相嵌套的关系,开线段树又无从下手,考虑肢解问题。

(显然从数论角度开始 yy。。)
对于模 p 数组,询问 (l, r, o):
o = 0, 则 [l, r] 区间至少存在一个 0 
o = x, 则 [l, r] 区间不能出现 0,最后一位总能找到一个逆元。

(对 10 分解后很自然会形成 模2 和 模5 两个子问题。。)
考虑一般的模 p 版本的子问题,观察后发现,对单个数只存在三种状态 :

0
[1, p) 任何一个数
[1, p) 中的某一个数(被各种等式固定住了。)

对于 0 的情况可以立刻判断出来,对后两种情况,可以固定任意一个数位后判断是否存在矛盾。
(因为对于状态 3,如果确实是某一个数。。可以通过默认一个数位后把这个数显示的通过等式表示出来。。)

。。。这样设计 dp[i] 表示第 i 个位置如果是 0 的时候存在的所有方案数,(在数组末尾再添加一个默认为 0 的虚坷垃以记录所有方案。)。。枚举的时候找前面其他可能是 0 的位置,然后累计 Count(l, r) 表示 l, r 区间所有的数都是状态 2 和状态 3 所可能的方案数。。

(。。这样就不存在询问之间的嵌套影响。。Count 函数返回的结果会是 p-1 的某个整次幂 或者 不合法返回 0。。)

总的复杂度大概是 O(n2m) 吧..

const int N = 50;

DB dp[N][N][N][N];

class RandomColoring {
public:
	double getProbability(int N, int maxR, int maxG, int maxB, int startR, int startG, int startB, int d1, int d2) {

		RST(dp), dp[0][startR][startG][startB] = 1;

		#define u dp[i][r][g][b]
		#define v dp[i+1][r+dr][g+dg][b+db]

		REP(i, N) REP(r, maxR) REP(g, maxG) REP(b, maxB) if (u){

		    int S = 0;

		    FOR(dr, max(-r, -d2), min(maxR-r, d2+1)) FOR(dg, max(-g, -d2), min(maxG-g, d2+1)) FOR(db, max(-b, -d2), min(maxB-b, d2+1)){
		        if (abs(dr) < d1 && abs(dg) < d1 && abs(db) < d1) continue;
                ++S;
		    }

		    FOR(dr, max(-r, -d2), min(maxR-r, d2+1)) FOR(dg, max(-g, -d2), min(maxG-g, d2+1)) FOR(db, max(-b, -d2), min(maxB-b, d2+1)){
		        if (abs(dr) < d1 && abs(dg) < d1 && abs(db) < d1) continue;
                v += u / S;
		    }
		}

        DB res = 0; int i = N - 1; REP(r, maxR) REP(g, maxG) REP(b, maxB) if (u){
            if (abs(r - startR) > d2 || abs(g - startG) > d2 || abs(b - startB) > d2 ||
                 abs(r - startR) < d1 && abs(g - startG) < d1 && abs(b - startB) < d1)
            res += u;
        }

		return res;
	}
};
const int N = 50;

DB dp[N][N][N][N], s[N+1][N+1][N+1];
int w[N][N][N], maxR, maxG, maxB;

DB sum(int x0, int y0, int z0, int x1, int y1, int z1){
    checkMin(++x1, maxR), checkMin(++y1, maxG), checkMin(++z1, maxB), checkMax(x0, 0), checkMax(y0, 0), checkMax(z0, 0);
    return s[x1][y1][z1] - s[x0][y1][z1] - s[x1][y0][z1] - s[x1][y1][z0] + s[x0][y0][z1] + s[x0][y1][z0] + s[x1][y0][z0] - s[x0][y0][z0];
}

class RandomColoring {
public:
	double getProbability(int N, int maxR, int maxG, int maxB, int startR, int startG, int startB, int d1, int d2) {

        ::maxR = maxR, ::maxG = maxG, ::maxB = maxB;
		RST(dp, w, s), dp[0][startR][startG][startB] = 1;

		REP_1(r, maxR) REP_1(g, maxG) REP_1(b, maxB) s[r][g][b] = r * g * b;
		REP(r, maxR) REP(g, maxG) REP(b, maxB) w[r][g][b] = sum(r-d2, g-d2, b-d2, r+d2, g+d2, b+d2) - (d1 ? sum(r-d1+1, g-d1+1, b-d1+1, r+d1-1, g+d1-1, b+d1-1) : 0);

		#define u dp[i][r-1][g-1][b-1]
		#define w w[r-1][g-1][b-1]
		#define v dp[i+1][r][g][b]

        --N; REP(i, N){

		    REP_1(r, maxR) REP_1(g, maxG) REP_1(b, maxB) {
		        if (u && w) u /= w;
                s[r][g][b] = u + s[r-1][g][b] + s[r][g-1][b] + s[r][g][b-1] - s[r-1][g-1][b] - s[r-1][g][b-1] - s[r][g-1][b-1] + s[r-1][g-1][b-1];
            }

            REP(r, maxR) REP(g, maxG) REP(b, maxB){
                v = sum(r-d2, g-d2, b-d2, r+d2, g+d2, b+d2) - (d1 ? sum(r-d1+1, g-d1+1, b-d1+1, r+d1-1, g+d1-1, b+d1-1) : 0);
            }
		}

		#undef u
        #define u dp[i][r][g][b]

        DB res = 0; int i = N; REP(r, maxR) REP(g, maxG) REP(b, maxB) if (u){
            if (abs(r - startR) > d2 || abs(g - startG) > d2 || abs(b - startB) > d2 ||
                 abs(r - startR) < d1 && abs(g - startG) < d1 && abs(b - startB) < d1)
            res += u;
        }

		return res;
	}
};
const int Null = -1;
const int N = 109;

VI L, R, O; VII adj[N];
int A[N], n, m, mod;


int _I(int x){
    FOR(i, 1, mod) if (i * x % mod == 1) return i;
}

#define v adj[u][i].first
#define r adj[u][i].second

bool dfs(int u, int val) {
    if (A[u] == Null) A[u] = val; else return A[u] == val;
    REP(i, SZ(adj[u])) if (!dfs(v, (u <= v ? r : _I(r)) * val % mod)) return false;
    return true;
}

#undef v
#undef r

int count(int l, int r) {

    if (r - l <= 0) return 1;

    REP(i, m){
        int a = L[i], b = R[i], o = O[i] % mod;
        if (!o && l <= a && b < r) return 0;
    }

    FOR_1(i, 0, n+1) CLR(adj[i]);

    REP(i, m){
        int a = L[i], b = R[i] + 1, o = O[i] % mod;
        if (a >= l && b <= r){
            adj[a].PB(MP(b, o));
            adj[b].PB(MP(a, o));
        }
    }

    int cnt = -1; FLC(A, Null);

    for (int i = l; i <= r; ++i) {
        if (A[i] == Null) {
            if (!dfs(i, 1)) return 0;
            ++cnt;
        }
    }

    return pow(mod-1, cnt);
}

int dp[N]; bool Z[N]; // could be zero ?

LL calc(int mod) {

    FLC(Z, true), ::mod = mod; REP_1(i, n) REP(j, m){
        int l = L[j], r = R[j], o = O[j] % mod;
        if (o && l <= i && i <= r){Z[i] = false; break;}
    }

    RST(dp), dp[0] = 1;

    REP_1(i, n + 1) if (Z[i]){
        REP(l, i) if (Z[l]) INC(dp[i], pdt(dp[l], count(l + 1, i))); //[l+1, i)
    }

    return dp[n+1];
}


class ProductQuery {
public:
    int theInput(int N, vector<int> Qfrom, vector<int> Qto, vector<int> output) {
        n = N, m = SZ(Qfrom), L = Qfrom, R = Qto, O = output; REP(i, m) ++L[i], ++R[i];
        return pdt(calc(2), calc(5));
    }
};