2015 ACM-ICPC Regional长春

A Too Rich

题目大意

现在有面值为1,5,10,20,50,100,200,500,1000,2000十种硬币,给定每种硬币的数量。你要选出尽量多的硬币,使得其面值恰好为 p ,无解输出-1。

数据范围

p109,数据组数 20000

题解

这题假如没有50,500两种硬币的话,相当于剩下 1,5,10,20,100,200,1000,2000 ,那么权值小的硬币的面值必为权值大的硬币的面值的约数。那么我们就可以直接贪心地确定每种硬币的数量。具体而言就是从大到小枚举,设当前枚举到第 i 种硬币,sum表示比当前硬币小的所有硬币所能凑出的最大权值, x 表示当前需要凑出的面值,那么若sumx,我们显然是不需要用第 i 种硬币的,因为都可以当成面值为1的硬币来用,否则,设 c=xsumVi ,则当前硬币至少需要用 c 这么多,假如无法整除,则c+1,因为保证面值大的尽量小,因此最终答案会尽量大。
但原题中还有 50,500 两种,但可以注意到的是,假如这两种硬币取了偶数次,必然就可以被面值小的表示出来了。因此,我们可以直接打个爆搜,每种硬币至少取到 c 的下界,并且允许多取一个。多取两个显然是没有意义的。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int Val[10] = {1,5,10,20,50,100,200,500,1000,2000};

int C[10],S[10],ans;

void Dfs(int Now,int Least,int Use)
{
    if (Least < 0) return;
    if (Least == 0)
    {
        ans = max(ans,Use);
        return;
    }
    int mx = Least - S[Now];
    if (mx < 0) mx = 0;
    int ch = mx / Val[Now];
    if (mx % Val[Now]) ch ++;
    if (ch <= C[Now])
        Dfs(Now - 1,Least - ch * Val[Now],Use + ch);
    ++ ch;
    if (ch <= C[Now])
        Dfs(Now - 1,Least - ch * Val[Now],Use + ch);
}

int main()
{
    int T;
    scanf("%d", &T);
    for(;T;T --)
    {
        int p;
        scanf("%d", &p);
        for(int i = 0;i < 10;i ++) 
            scanf("%d", &C[i]);
        for(int i = 1;i <= 10;i ++)
            S[i] = S[i - 1] + C[i - 1] * Val[i - 1];
        ans = -1;
        Dfs(9,p,0);
        printf("%d\n", ans);
    }
    return 0;
}

B Count a * b

题目大意

f(n)=i=1n1j=1n1[(ij)modn0]

g(n)=d|nf(d)

多组询问,给定 n ,求g(n),答案对 264 取模。

数据范围

n109 20000 组询问

题解

首先,我们可以比较轻易的知道:
f(n)=n2ni=1gcd(i,n) ,具体的原理就是总的剪掉不合法的,对于每个 i ,不合法的j的数量就是 nngcd(i,n)=gcd(i,n)
t(n)=ni=1gcd(i,n) ,那么有 t(n)=d|ndϕ(nd) t=idϕ
那么我们可以得到

g(n)=d|nf(d)=d|n(d2t(d))=d|nd2d|nt(d)=d|nd2(idϕ1)(n)=d|nd2(idid)(n)=d|n(d2n)

很显然 g(n) 不是积性函数,而 d|nd2 d|nn 都是积性的,因此我们可以分开计算。
但我们始终需要分解质因数,一种是直接用pollard rho来分解,本题中预处理出 <n 的质数表暴力分解就好了。

比较好的时间复杂度为 O(Tn14) ,我的程序是 O(Tn12logn)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 32000;

typedef unsigned long long ULL;

int Pri[MAXN],cnt;

ULL sqr(ULL a) {return a * a;}

ULL Calc(ULL c,int t)
{
    ULL ans = 0;
    for(ULL v = 1;t >= 0;t --,v = v * c * c)
        ans += v;
    return ans;
}

int main()
{
    for(int i = 2,n = MAXN - 5;i <= n;i ++)
    {
        bool f = 1;
        for(int j = 2;j * j <= i;j ++)
            if (i % j == 0) {f = 0;break;}
        if (f) Pri[++ cnt] = i;
    }
    int T;
    scanf("%d", &T);
    for(;T;T --)
    {
        int n;
        scanf("%d", &n);
        ULL ans1 = 1,ans2 = 1;
        for(int i = 1;Pri[i] * Pri[i] <= n;i ++)
        {
            ULL c = 1,ti = 0;
            for(;n % Pri[i] == 0;n /= Pri[i]) c *= Pri[i], ++ ti;
            ans1 *= c * (ti + 1);
            ans2 *= Calc(Pri[i], ti);
        }
        if (n > 1)
        {
            ans1 *= n * 2;
            ans2 *= Calc(n,1);
        }
        printf("%llu\n", ans2 - ans1);
    }   
    return 0;
}

C Play a game

题目大意

现在有一个双方轮流操作的游戏。具体而言就是一开始有一个串 S 和字符串集合A,每一回合,当前操作的人可以选择:
1. 删掉 S 的开头
2. 删掉S的结尾
若当前操作者操作后使 S 变为空串或SA时,则他获胜了。
现在给定一个字符串 T 与字符串集合A,有 Q 个询问l,r,询问当 S T[l..r]时,先手必胜还是后手必胜。

数据范围

|T|40000,sA|s|10000,Q40000 ,字符集为小写字母。

题解

一个最简单的dp就是设 f[l][r] 表示 S=T[l..r] 时先手获胜情况。转移十分简单, f[l][r]=¬f[l+1][r]¬f[l][r1] 。然后当 T[l..r] 为空串或出现在 A 中则将f[l][r]设为0。但这种做法是 O(|T|2) ,怎么都不能过。

但注意到 f[l][r] 的值是 0 1,这启发我们用bitset去优化。但直接这样优化应该是有BUG的,因此不妨改变定义,设 f[l][i] 表示当前的 S T[i..i+l1]
转移同理, f[l][i]=¬f[l1][i]¬f[l1][i+1] ,那么用bitset就是 f[l]=¬f[l1](f[l1]>>1)
唯一剩下的问题就是怎么赋初值,暴力做还是 O(|T|2) 的,但由于 sA|s|10000 ,因此,我们可以分 |s|100 |s|>100 两种情况考虑。对于 l100 ,暴力枚举每个位置即可。对于 |s|>100 ,只有不超过 100 个串,还是对于每个串暴力枚举每个位置即可。
总的时间复杂度就是 O(|T|232+|T|100+Q) (这里复杂度里写了常数非常不标准。。)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <vector>

using namespace std;

const int MAXN = 40001;
const int G[2] = {37,53};

typedef unsigned long long ULL;

struct Node
{
    int To[26],fal;
}T[MAXN];

vector<int> Lk[MAXN];
bitset<MAXN> F[MAXN];
char S[MAXN];
bool Fal[MAXN][105];
ULL Pow[2][MAXN],Has[2][MAXN],Inp[2][MAXN];
int N,M,Q,cnt,tot,Len[MAXN];

void Work()
{
    scanf("%d%d%d", &N, &M, &Q);
    scanf("%s", S + 1);
    memset(T,0,sizeof T);
    cnt = 1;
    for(int i = 0;i <= 40000;i ++) Lk[i].clear();
    for(int i = 0;i < 2;i ++)
        for(int j = 1;j <= N;j ++)
            Has[i][j] = Has[i][j - 1] * G[i] + S[j] - 'a' + 1;
    tot = 0;
    for(int i = 1;i <= M;i ++)
    {
        static char Tr[MAXN];
        scanf("%s", Tr + 1);
        int l = strlen(Tr + 1);
        if (l <= 100)
        {
            int cur = 1;
            for(int j = 1;j <= l;j ++)
            {
                int x = Tr[j] - 'a';
                if (!T[cur].To[x]) T[cur].To[x] = ++ cnt;
                cur = T[cur].To[x];
            }
            T[cur].fal = 1;
        } else
        {
            ++ tot;
            Len[tot] = l;
            for(int j = 0;j < 2;j ++)
            {
                Inp[j][tot] = 0;
                for(int k = 1;k <= l;k ++)
                    Inp[j][tot] = Inp[j][tot] * G[j] + Tr[k] - 'a' + 1;
            }
            Lk[l].push_back(tot);
        }
    }
    for(int i = 1;i <= N;i ++)
    {
        int cr = 1;
        memset(Fal[i],0,sizeof Fal[i]);
        for(int l = 1,p = i;l <= 100 && p <= N;l ++,p ++)
        {
            cr = T[cr].To[S[p] - 'a'];
            Fal[i][l] = T[cr].fal;
        }
    }
    for(int l = 1;l <= N;l ++)
    {
        F[l] = ~F[l - 1];
        F[l] = F[l] | ~(F[l - 1] >> 1);
        if (l <= 100)
        {
            for(int i = 1;i <= N;i ++) 
                if (Fal[i][l]) F[l][i] = 0;
        } else
        {
            for(int i = 0;i < Lk[l].size();i ++)
            {
                int v = Lk[l][i];
                for(int j = 1;j <= N;j ++)
                {
                    int r = j + l - 1;
                    if (r > N) break;
                    bool f = 1;
                    for(int k = 0;k < 2;k ++)
                        if (Has[k][r] - Has[k][j - 1] * Pow[k][r - j + 1] != Inp[k][v]) {f = 0;break;}
                    if (f) F[l][j] = 0;
                }
            }
        }
    }
    for(int i = 1;i <= Q;i ++)
    {
        int l,r;
        scanf("%d%d", &l, &r);
        printf("%d\n", int(F[r - l + 1][l]));
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    for(int i = 0;i < 2;i ++)
    {
        Pow[i][0] = 1;
        for(int j = 1;j <= 40000;j ++)
            Pow[i][j] = Pow[i][j - 1] * G[i];
    }
    for(;T;T --) Work();
    return 0;
}

D Pipes selection

题目大意

给定 n 个数ai,令 s=ni=1ai ,对于所有 j[1,s] ,令集合 Tj 表示所有的二元组 (lk,rk),lkrk 满足 rki=lkai=j 。二元组的大小关系定义为字典序。现在需要对于每个 j ,找到排名为|Tj|+12的二元组。

数据范围

ai0
1max(n,s)30000

题解

pi=ij=1ai ,那么显然,问题就变成了找到 l,r 使得 prpl=j 。可以发现的是,假如对于每个 j ,我们找到其中位二元组的l后, r 是很好确定了,那么问题就变为如何找到这个l
不妨对序列分块,令每一块的长度为 L
我们现在的目标是确定出每个j l 所在的块。先开一个桶G Gi 表示 pj=i j 的数量。接下来暴力枚举每个块Bi,开一个桶 C 存储在Bi前的信息,也就是说 Ci 表示 pj=i j<Bi j 的数量。那么我们对C GC 用FFT做一次类似于卷积的东西,就可以确定出 l Bi前的使得 prpl=j 的数量,那么我们就可以确定每个 j l在哪了。最后对于每个 j 在其块中暴力枚举出合法的l即可。确定 r 可以一开始对每个pr维护一个 set 存位置即可。
时间复杂度为 O(Ls+NLslogs) ,令 L=Nlogs 取得最好的复杂度 O(sNlogs)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#define Clear(a) (memset(a,0,sizeof a))

using namespace std;

const int MAXN = 65540,Mo = int(1e9) + 7;
const double PI = acos(-1);

struct Comp
{
    double a,b;

    Comp(void){}
    Comp(double x,double y) : a(x),b(y){}
};

Comp operator +(const Comp &a,const Comp &b) {return Comp(a.a + b.a,a.b + b.b);}
Comp operator -(const Comp &a,const Comp &b) {return Comp(a.a - b.a,a.b - b.b);}
Comp operator *(const Comp &a,const Comp &b) {return Comp(a.a * b.a - a.b * b.b,a.a * b.b + a.b * b.a);}

vector<int> Lk[MAXN];
Comp W[MAXN];
int S[MAXN],Kth[MAXN],Ac[MAXN],Cur[MAXN],Tmp[MAXN],Li[MAXN],R[MAXN],N,L,s,LIM;

void Dft(Comp *a,int sig)
{
    static Comp tmp[MAXN];
    for(int i = 0;i < L;i ++)
    {
        int t = 0;
        for(int j = i,c = 1;c < L;c <<= 1,j >>= 1) t = ((t << 1) | (j & 1));
        tmp[t] = a[i];
    }
    for(int m = 2;m <= L;m <<= 1)
        for(int i = 0,half = m / 2;i < half;i ++)
        {
            Comp w = W[sig > 0 ? (i * (L / m)) : (L - i * (L / m))];
            for(int j = i;j < L;j += m)
            {
                Comp u = tmp[j],v = w * tmp[j + half];
                tmp[j] = u + v;
                tmp[j + half] = u - v;
            }
        }
    for(int i = 0;i < L;i ++) a[i] = tmp[i];
}

void Multi(int *A,int *B,int *C)
{
    static Comp a[MAXN];    
    for(int i = 0;i < L;i ++) a[i] = Comp(A[i] + B[i],A[i] - B[i]);
    Dft(a,1);
    for(int i = 0;i < L;i ++) a[i] = a[i] * a[i];
    Dft(a,-1);
    for(int i = 0;i < L;i ++) C[i] = int((a[i].a / L + 0.5)) / 4;
}

void Locate(int l,int r,int j)
{
    for(int p = l;p <= r;p ++)
        if (Kth[j] > Ac[S[p - 1] + j]) Kth[j] -= Ac[S[p - 1] + j]; else
        {
            int l = Kth[j];
            Li[j] = p;
            R[j] = Lk[S[p - 1] + j][l - 1];
            break;
        }
}

void Work()
{
    Clear(Lk);
    scanf("%d", &N);
    for(int i = 1;i <= N;i ++)
        scanf("%d", &S[i]), S[i] += S[i - 1],Lk[S[i]].push_back(i);
    for(L = 1;L <= S[N] * 2;L <<= 1);
    for(int i = 0;i <= L;i ++)
        W[i] = Comp(cos(2 * PI * i / L),sin(2 * PI * i / L));
    int LIM = sqrt(2 * N * log2(L));
    Clear(Ac),Clear(Cur);
    for(int i = 1;i <= N;i ++) Ac[S[i]] ++,Cur[S[N] - S[i - 1]] ++;
    Multi(Ac,Cur,Tmp);
    for(int i = 1;i <= S[N];i ++) Kth[i] = (Tmp[i + S[N]] + 1) / 2;
    Clear(Cur);
    Clear(Li);
    Clear(R);
    for(int i = 1;i <= N;i += LIM)
    {
        int r = min(i + LIM - 1,N);
        for(int j = 0;j <= S[N];j ++) Cur[j] = 0;
        for(int j = i;j <= r;j ++) Cur[S[N] - S[j - 1]] ++;
        Multi(Ac,Cur,Tmp);
        for(int j = 1;j <= S[N];j ++)
            if (!Li[j])
            {
                if (Tmp[j + S[N]] >= Kth[j] && Tmp[j + S[N]])
                    Locate(i,r,j); else Kth[j] -= Tmp[j + S[N]];
            }
    }
    int ans1 = 0,ans2 = 0;
    for(int i = 1,p = 1;i <= S[N];i ++)
    {
        p = p * 233LL % Mo;
        ans1 = (ans1 + Li[i] * 1ll * p) % Mo;
        ans2 = (ans2 + R[i] * 1ll * p) % Mo;
    }
    printf("%d %d\n", ans1, ans2);
}

int main()
{
    int T;
    scanf("%d", &T);
    for(;T;T --) Work();
    return 0;
}

I Chess Puzzle

题目大意

现在给定一个 NM 的网格图,有些格子已经染上了颜色,颜色只有黑或白两种。你要把剩下的格子染上颜色。对于一副网格图,假如存在两个格子 (i,j),(x,y) ,满足:
1. (i,j) 被染成黑色, (x,y) 被染成白色;
2. |ix|=A,|jy|=B
那么就能获得1的价值,网格图的价值是所有能获得的价值的总和。
现在给定 N,M,A,B 以及网格图的初始状态,需要求出最大可能的价值,以及在此条件下的字典序最小的染色方案。

数据范围

1n,m100

题解

将格子看成一个点,假如两个格子 (i,j),(x,y) 满足 |ix|=A,|jy|=B ,那么就在新图中其对应的点连上一条边。那么显然,这副图是一个二分图。对这幅图进行二分图染色,那么接下来就是一个非常简单的二元关系了,构出图后用总收益减去最小割即可。剩下的问题就是怎么找到字典序最小的方案,这也是比较简单的。因为是割,这相当于要我们将一些既可以与原点连也可以与汇点连的点分配其所属集合。我们可以贪心来考虑,假如当前一个点已经被确定所属集合,我们就不用管了,否则我们将这个点归属较优的一边,顺便更新其他点的归属情况。
总的复杂度是 O(maxflow(nm,nm))

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 10005;

struct Node
{
    int To,Next,Flow;

    Node(void){}
    Node(int a,int b,int c) : To(a),Next(b),Flow(c){}
}E[MAXN * 30];

vector<int> Lk[MAXN];
char Ch[105];
int Final[MAXN],D[MAXN],Col[MAXN],Cnt[MAXN],Bel[MAXN],N,M,A,B,S,T,tot;

int Id(int x,int y)
{
    return (x - 1) * M + y;
}

void Link(int u,int v,int f)
{
    E[++ tot] = Node(v,Final[u],f),Final[u] = tot;
    E[++ tot] = Node(u,Final[v],0),Final[v] = tot;
}

bool In(int x,int y)
{
    return x > 0 && x <= N && y > 0 && y <= M;
}

void Dfs_C(int Now,int c)
{
    Col[Now] = c;
    for(int i = 0;i < Lk[Now].size();i ++)
        if (Col[Lk[Now][i]] == -1) Dfs_C(Lk[Now][i], !c);
}

bool Bfs()
{
    static int Q[MAXN];
    for(int i = S;i <= T;i ++) D[i] = -1;
    int fi = 1,en = 1;
    Q[1] = S;
    D[S] = 0;
    for(;fi <= en;fi ++)
    {
        int u = Q[fi];
        for(int i = Final[u];i;i = E[i].Next)
            if (E[i].Flow > 0 && D[E[i].To] < 0)
            {
                D[E[i].To] = D[u] + 1;
                Q[++ en] = E[i].To;
            }
    }
    return D[T] > -1;
}

int Dfs(int Now,int Flow)
{
    if (Now == T) return Flow;
    int Use = 0;
    for(int i = Final[Now];i;i = E[i].Next)
        if (E[i].Flow > 0 && D[E[i].To] == D[Now] + 1)
        {
            int tmp = Dfs(E[i].To,min(E[i].Flow,Flow - Use));
            Use += tmp,E[i].Flow -= tmp,E[i ^ 1].Flow += tmp;
            if (Use == Flow) return Use;
        }
    return Use;
}

void Walk(int Now)
{
    Bel[Now] = 1;
    for(int i = Final[Now];i;i = E[i].Next)
        if (!Bel[E[i].To] && E[i].Flow) Walk(E[i].To);
}

void Walk_R(int Now)
{
    Bel[Now] = 2;
    for(int i = Final[Now];i;i = E[i].Next)
        if (!Bel[E[i].To] && E[i ^ 1].Flow) Walk_R(E[i].To);
}

void Work()
{
    tot = 1;
    memset(Lk,0,sizeof Lk);
    memset(Final,0,sizeof Final),memset(Col,255,sizeof Col);
    scanf("%d%d%d%d", &N, &M, &A, &B);
    int ans = 0;
    for(int i = 1;i <= N;i ++)
        for(int j = 1;j <= M;j ++)
        {
            Cnt[Id(i,j)] = 0;
            for(int d = -1;d <= 1;d += 2)
                for(int d1 = -1;d1 <= 1;d1 += 2)
                {
                    int x = i + d * A,y = j + d1 * B;
                    if (In(x,y))
                    { 
                        Lk[Id(i,j)].push_back(Id(x,y)),Cnt[Id(i,j)] ++;
                        Link(Id(i,j),Id(x,y),2);
                        ans += 2;
                    }
                }
        }
    int n = N * M;
    for(int i = 1;i <= n;i ++)
        if (Col[i] == -1) Dfs_C(i,Col[i] = 0);
    S = 0,T = n + 1;
    for(int i = 1;i <= N;i ++)
    {
        scanf("%s", Ch + 1);
        for(int j = 1;j <= M;j ++)
        {
            int p = Id(i,j);
            if (Ch[j] == '.') Link(S,p,Cnt[p]),Link(p,T,Cnt[p]); else
            {
                if (!Col[p]) 
                    Link(S,p,Ch[j] == 'B' ? 1 << 30 : Cnt[p]),Link(p,T,Ch[j] == 'W' ? 1 << 30 : Cnt[p]); else
                        Link(S,p,Ch[j] == 'W' ? 1 << 30 : Cnt[p]),Link(p,T,Ch[j] == 'B' ? 1 << 30 : Cnt[p]);
            }
        }
    }
    while (Bfs()) ans -= Dfs(S,1 << 30);
    memset(Bel,0,sizeof Bel);
    Walk(S),Walk_R(T);
    for(int i = 1;i <= n;i ++)
    {
        if (Bel[i]) continue;
        if (!Col[i]) Walk(i); else Walk_R(i);
    }
    printf("%d\n", ans / 2);
    for(int i = 1;i <= N;i ++)
    {
        for(int j = 1;j <= M;j ++)
        {
            int p = Id(i,j);
            if (!Col[p])
            {
                printf("%c", Bel[p] == 2 ? 'W' : 'B');
            } else
                printf("%c", Bel[p] == 2 ? 'B' : 'W');
        }
        printf("\n");
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    for(;T;T --) 
        Work();
    return 0;
}

K Maximum Spanning Forest

题目大意

现在有一副无限大的格点图。一开始每个格点都是孤立的。现在会加入 n 条边,具体形式是:(a,b),(x,y),c,表示在以
(a,b) 为左下角, (x,y) 为右上角的矩形中的所有格点都向四周相邻的格点连上一条权值为 c 的边。
加入每条边后,你都需要求出当前这幅图的最大生成树的权值。答案对109+7取模。

数据范围

n100 ,所有输入的数都满足在 [1,2×109]

题解

首先将读入的所有坐标离散化。那么现在相当于在一个 nn 的网格图上做了。对于每条网格图上的边,记录当前能覆盖他的最大边权,设 n 表示这条边上实际有的点数,那么我们可以先连上n2条边,剩一个点去和外部连边。对于一个格子内部的点也是同理。接下来就是如何处理这幅网格图的最大生成树了。直接每次 n3 条边跑估计会挂,实际上每次加入边后只需要保留当前在生成树上的边,并且按从大到小排。那么每次加入 n2 条边时,直接插入排序,再跑生成树即可。
总的复杂度是 O(n3α(n))

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define pb push_back

using namespace std;

const int MAXN = 505 * 505,Mo = int(1e9) + 7;

typedef long long LL;

struct Edge
{
    int u,v,c;

    Edge(void){}
    Edge(int a,int b,int c) : u(a),v(b),c(c){}
}E[MAXN];

struct Info
{
    int x,y,x1,y1,c;
}A[MAXN];

int Fa[MAXN],Ref[505][505],R[505][505],U[505][505],Self[505][505],N;

int Get(int a) {return Fa[a] == a ? a : Fa[a] = Get(Fa[a]);}

void Merge(int u,int v) {Fa[Get(u)] = Get(v);}

void Work()
{
    vector<int> X,Y;
    X.clear(),Y.clear();
    scanf("%d", &N);
    for(int i = 1;i <= N;i ++)
    {
        scanf("%d%d%d%d%d", &A[i].x, &A[i].y, &A[i].x1, &A[i].y1, &A[i].c);
        X.pb(A[i].x),X.pb(A[i].x1),Y.pb(A[i].y),Y.pb(A[i].y1);
    }
    sort(X.begin(),X.end());
    sort(Y.begin(),Y.end());
    X.erase(unique(X.begin(),X.end()),X.end()),Y.erase(unique(Y.begin(),Y.end()),Y.end());
    for(int i = 1;i <= N;i ++)
    {
        A[i].x = lower_bound(X.begin(),X.end(),A[i].x) - X.begin();
        A[i].x1 = lower_bound(X.begin(),X.end(),A[i].x1) - X.begin();
        A[i].y = lower_bound(Y.begin(),Y.end(),A[i].y) - Y.begin();
        A[i].y1 = lower_bound(Y.begin(),Y.end(),A[i].y1) - Y.begin();
    }
    for(int i = 0;i < X.size();i ++)
        for(int j = 0;j < Y.size();j ++)
        {
            Ref[i][j] = i * Y.size() + j;
            R[i][j] = U[i][j] = Self[i][j] = 0;
        }
    int tot = 0,ntot = 0,n = X.size(),m = Y.size();
    static Edge Bak[MAXN];
    for(int p = 1;p <= N;p ++)
    {
        for(int i = A[p].x;i <= A[p].x1;i ++)
            for(int j = A[p].y;j <= A[p].y1;j ++) 
            {
                if (j < A[p].y1) R[i][j] = max(R[i][j],A[p].c);
                if (i < A[p].x1) U[i][j] = max(U[i][j],A[p].c);
                if (i < A[p].x1 && j < A[p].y1) Self[i][j] = max(Self[i][j],A[p].c); 
            }
        int Ans = 0;
        for(int i = 0;i < n;i ++)
            for(int j = 0;j < m;j ++)
            {
                if (j + 1 < m) (Ans += R[i][j] * 1ll * (Y[j + 1] - Y[j] - 1) % Mo) %= Mo;
                if (i + 1 < n) (Ans += U[i][j] * 1ll * (X[i + 1] - X[i] - 1) % Mo) %= Mo;
                if (i + 1 < n && j + 1 < m) 
                    (Ans += Self[i][j] * ((X[i + 1] - X[i] - 1) * 1ll * (Y[j + 1] - Y[j] - 1) % Mo) % Mo) %= Mo;
            }
        E[0] = Edge(0,0,1 << 30),E[++ tot] = Edge(0,0,-(1 << 30));
        for(int i = 0;i < n * m;i ++) Fa[i] = i;
        for(int i = 1;i <= tot;i ++)
        {
            int u,v;
            if (E[i - 1].c > A[p].c && A[p].c >= E[i].c)
            {
                for(int x = A[p].x;x <= A[p].x1;x ++)
                    for(int y = A[p].y;y <= A[p].y1;y ++)
                    {
                        if (y < A[p].y1)
                        {
                            u = Ref[x][y],v = Ref[x][y + 1];
                            if (Get(u) == Get(v)) continue;
                            Merge(u,v);
                            (Ans += A[p].c) %= Mo;
                            Bak[++ ntot] = Edge(u,v,A[p].c);
                        }
                        if (x < A[p].x1)
                        {
                            u = Ref[x][y],v = Ref[x + 1][y];
                            if (Get(u) == Get(v)) continue;
                            Merge(u,v);
                            (Ans += A[p].c) %= Mo;
                            Bak[++ ntot] = Edge(u,v,A[p].c);
                        }
                    }
            }
            if (i == tot) continue;
            u = E[i].u,v = E[i].v;
            if (Get(u) == Get(v)) continue;
            Merge(u,v);
            (Ans += E[i].c) %= Mo;
            Bak[++ ntot] = E[i];
        }
        printf("%d\n", Ans);
        for(int i = 1;i <= ntot;i ++) E[i] = Bak[i];
        tot = ntot;
        ntot = 0;
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    for(;T;T --) Work();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值