BITACM2018第二轮积分赛(一)题解

A - 一道可持久化并查集好题

出题人: z h b e r zhber zhber

通过/提交: 17 / 37 17/37 17/37

题解: 此题数据极小,直接模拟即可。撤销操作就是不执行前面 k k k 次操作,把撤销操作视为返回 k k k 次之前的历史版本。用state[i][j][k] 表示第 i i i 次操作后第 j j j 个点和第 k k k 个点是否联通,对于 1 1 1 操作,复制上一次操作之后的状态,然后考虑新增的经过 a − b a-b ab 边而可到达的点对。这些新点对(记为x^y)一定是 x x x 在一侧, y y y 在另一侧,由于新加了 a − b a-b ab 而联通。那么分别找 a , b a,b a,b 能到达的点放到两集合内,取两个集合各一点配对都设为联通即可 O ( n 2 ) O(n^2) O(n2) 。或者直接floyd传递闭包 O ( n 3 ) O(n^3) O(n3) 。对于第 2 2 2 种操作,复制上一次的状态,暴力找和它联通的点数。对于第三种操作,复制的不是上一次的状态,而是第 x − k − 1 x-k-1 xk1 次的状态。时间复杂度 O ( n 2 m ) O(n^2m) O(n2m) ,空间复杂度 O ( n 2 m ) O(n^2m) O(n2m)

#include<bits/stdc++.h>
#define maxn 100 + 10
using namespace std;
int state[maxn][maxn][maxn];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) state[0][i][i]=1;
    for (int i=1;i<=m;i++)
    {
        int op;scanf("%d",&op);
        if (op==1)
        {
            for (int j=1;j<=n;j++)
                for (int k=1;k<=n;k++)
                    state[i][j][k]=state[i-1][j][k];
            int a,b;scanf("%d%d",&a,&b);
            vector<int>A,B;A.clear();B.clear();
            for (int j=1;j<=n;j++)
            {
                if (state[i][a][j])A.push_back(j);
                if (state[i][b][j])B.push_back(j);
            }
            for (int j=0;j<A.size();j++)
                for (int k=0;k<B.size();k++)
                {
                    state[i][A[j]][B[k]]=1;
                    state[i][B[k]][A[j]]=1;
                }
        }else if (op==2)
        {
            for (int j=1;j<=n;j++)
                for (int k=1;k<=n;k++)
                    state[i][j][k]=state[i-1][j][k];
            int a,sum=0;scanf("%d",&a);
            for (int j=1;j<=n;j++)if (state[i][j][a])sum++;
            printf("%d\n",sum);
        }else if (op==3)
        {
            int k;scanf("%d",&k);
            for (int j=1;j<=n;j++)
                for (int l=1;l<=n;l++)
                    state[i][j][l]=state[i-k-1][j][l];
        }
    }
}

B - zhb love math

出题人: s t r a w b e r r y strawberry strawberry

通过/提交: 1 / 2 1/2 1/2

题解: 考虑每个元素 b i b_i bi 对答案的贡献,一定是一些包含 b i b_i bi 的区间,且区间除了它以外,没有 b j b_j bj b i b_i bi 的因数。容易想到的是,对每个 b i b_i bi 找到包含它可行区间的最左边 l i l_i li 和包含它可行区间的最右边 r i r_i ri 那么任何区间 [ l , r ]   l , r ∈ [ l i , r i ] [l,r]\ l,r∈[l_i,r_i] [l,r] l,r[li,ri] 都是答案,这样的区间有 ( i − l i + 1 ) × ( r i − i + 1 ) (i-l_i+1)×(r_i-i+1) (ili+1)×(rii+1) 个,朴素的 O ( n 2 ) O(n^2) O(n2) 想法是从 i i i 开始往左往右扫,找到第一个 b j % b i = 0 b_j\%b_i=0 bj%bi=0 j j j 就是边界,因为 n ≤ 1 0 5 n\leq 10^5 n105,是不能通过这题的。考虑怎么优化,当枚举到 i i i 的时候, 对于 i i i 左侧的 b j b_j bj 如果 b j % b i = 0 b_j\%b_i=0 bj%bi=0 那么对于位置 j j j r j r_j rj 最右只能到 ( i − 1 ) (i-1) (i1) 的位置!, 因为 b i ≤ 1 0 4 b_i\leq 10^4 bi104 ,所以可以直接枚举 b i b_i bi 的倍数 j j j 去更新答案,从左往右扫以 pre[i] 表示 i i i 最后出现的位置,对于 b i b_i bi 如果出现过其倍数 j j j,就有 r[pre[j]]=i-1 。同理扫着扫一遍对

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值