• 7.26 Test——DS2


    T1:

    加帕里的聚会

    256MB / 1s ; japari.cpp / c / pas / in / out

    【题目描述】

    加帕里公园里有n个区域,n-1条道路将它们连接到了一起,形成了一个树的结构。开始时,第i个区域有Ai个friends,但是由于砂之星的作用,有时从x区域到y区域的简单路径上的所有区域的friends数量都会增加v,有时从x区域到y区域的简单路径上所有区域的friends数量都会变成v。

    有时,从x区域到y区域的简单路径上所有区域的friends想要聚会,聚会时需要从所有参加聚会的friends中选择一部分作为staff。加帕里的friends都很喜欢质数,因此她们希望staff和非staff的参与者的数量都是质数。

    请你告诉她们,每次聚会是否存在一种方案满足她们的要求。

     

    【输入格式】

    第一行两个整数n,m,表示friends数量和事件数量。

    接下来一行n个数,第i个数表示Ai。

    接下来n-1行,每行2个数x,y,表示x和y之间有一条无向道路。

    接下来m行,每行第一个整数opt表示事件类型。

    若opt=1,接下来三个整数x,y,v,表示从x区域到y区域的简单路径上的所有区域的friends数量都增加v。

    若opt=2,接下来三个整数x,y,v,表示从x区域到y区域的简单路径上的所有区域的friends数量都变为v。

    若opt=3,接下来两个整数x,y,表示从x区域到y区域的简单路径上的所有区域的friends进行聚会。

     

    【输出格式】

    对于每个3事件,若可以让staff和非staff的数量都为质数,输出一行SUGOI,否则输出一行TANOSHI。

     

    【样例数据】

    japari1.in

    japari1.out

    3 4

    1 2 1

    2 1

    3 2

    2 2 1 4

    1 3 3 -1

    2 2 2 2

    3 1 3

    SUGOI

    japari2.in

    japari2.out

    4 6

    2 4 0 0

    2 1

    3 2

    4 3

    2 1 4 2

    1 4 4 9

    1 3 2 -2

    2 4 2 5

    3 1 4

    3 4 4

    TANOSHI

    SUGOI

     

    【样例解释】

    在第一个样例中,三个区域的friends数量依次为4、2、0,询问的friends和为6,可以分成两组,每组的friends数量都为3。

    在第二个样例中,四个区域的friends数量依次为2、5、5、5,第一个询问的friends和为17,无法分成两组。第二个询问的friends和为5,可以分为两组,每组的friends数量分别为2、3。

     

    【数据范围】

    对于30%的数据,n,m≤5000。

    对于另30%的数据,对于i>1,第i个区域与第i-1个区域直接相连。

    对于100%的数据,1≤n,m≤100000,1≤x,y≤n,一直满足0≤Ai,S≤10000000。在增加事件中v可能为负数。

    解析:

      签到题,标准树剖, 但我写挂了,一看代码,发现我没有统计子树大小,相当于我得到了一个普通的dfs序,这才几个月没打树剖就出错了

     代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 100005, inf = (1<<30);
    
    template<class T>
    inline void read(T &re)
    {
        T f=1;char c;
        while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1;
        re=c-'0';
        while((c=getchar())&&c>='0'&&c<='9')re = (re<<3) + (re<<1) + c - '0';
        re*=f; 
    }
    
    int n, m, a[maxn], ednum;
    
    vector<int> G[maxn];
    
    int root, tot;
    struct seg_tree{
        int ls, rs;
        int sum, cov, add;
    }tr[maxn<<2];
    
    int dep[maxn], top[maxn], pos[maxn], f[maxn], son[maxn], d[maxn], siz[maxn], cnt;
    
    int pri[maxn*10], num;
    bool prime[maxn*100];
    
    void Euler()
    {
        for(int i = 2; i <= 10000000; ++i)
        {
            if(!prime[i])
                pri[++num] = i;
            for(int j = 1; j <= num; ++j)
            {
                if(i * pri[j] > 10000000)    break;
                prime[i * pri[j]] = 1;
                if(i % pri[j] == 0)    break;
            }
        }
    }
    
    void dfs1(int x)
    {
        siz[x] = 1;
        for(unsigned int i = 0; i < G[x].size(); ++i)
        {
            int id = G[x][i];
            if(id == f[x])    continue;
            dep[id] = dep[x] + 1;
            f[id] = x;
            dfs1(id);
            if(siz[id] > siz[son[x]])
                son[x] = id;
            siz[x] += siz[id];
        }
    }
    
    void dfs2(int x)
    {
        pos[x] = ++cnt;
        d[cnt] = a[x];
        if(son[f[x]] == x)
            top[x] = top[f[x]];
        else
            top[x] = x;
        if(son[x])
            dfs2(son[x]);
        for(unsigned int i = 0; i < G[x].size(); ++i)
        {
            int id = G[x][i];
            if(id == f[x] || id == son[x])    continue;
            dfs2(id);
        }
    }
    
    void update(int x)
    {
        int l = tr[x].ls, r = tr[x].rs;
        tr[x].sum = tr[l].sum + tr[r].sum;
    }
    
    void build(int &x, int l, int r)
    {
        x = ++tot;
        tr[x].cov = -inf;
        if(l == r)
        {
            tr[x].sum = d[l];
            return ;
        }
        int mid = (l + r)>>1;
        build(tr[x].ls, l, mid);
        build(tr[x].rs, mid + 1, r);
        update(x);
    }
    
    void spread(int x, int L, int mid, int R)
    {
        int l = tr[x].ls, r = tr[x].rs;
        if(tr[x].cov != -inf)
        {
            tr[l].sum = (mid - L + 1) * tr[x].cov;
            tr[r].sum = (R - mid) * tr[x].cov;
            tr[l].cov = tr[r].cov = tr[x].cov;
            tr[l].add = tr[r].add = 0;
            tr[x].cov = -inf;
        }
        if(tr[x].add)
        {
            tr[l].sum += (mid - L + 1) * tr[x].add;
            tr[r].sum += (R - mid) * tr[x].add;
            tr[l].add += tr[x].add;
            tr[r].add += tr[x].add;
            tr[x].add = 0;
        }
    }
    
    void Add(int x, int l, int r, int L, int R, int v)
    {
        if(l <= L && R <= r)
        {
            tr[x].sum += v * (R - L + 1);
            tr[x].add += v;
            return ;
        }
        int mid = (L + R)>>1;
        spread(x, L, mid, R);
        if(l <= mid)
            Add(tr[x].ls, l, r, L, mid, v);
        if(mid < r)
            Add(tr[x].rs, l, r, mid + 1, R, v);
        update(x);
    }
    
    void Cover(int x, int l, int r, int L, int R, int v)
    {
        if(l <= L && R <= r)
        {
            tr[x].sum = v * (R - L + 1);
            tr[x].cov = v;
            tr[x].add = 0;
            return ;
        }
        int mid = (L + R)>>1;
        spread(x, L, mid, R);
        if(l <= mid)
            Cover(tr[x].ls, l, r, L, mid, v);
        if(mid < r)
            Cover(tr[x].rs, l, r, mid + 1, R, v);
        update(x);
    }
    
    int Query(int x, int l, int r, int L, int R)
    {
        if(l <= L && R <= r)
            return tr[x].sum;
        int mid = (L + R)>>1;
        spread(x, L, mid, R);
        int ret = 0;
        if(l <= mid)
            ret += Query(tr[x].ls, l, r, L, mid);
        if(mid < r)
            ret += Query(tr[x].rs, l, r, mid + 1, R);
        update(x);
        return ret;
    }
    
    void work1(int u, int v, int x)
    {
        while(top[u] != top[v])
        {
            if(dep[top[u]] > dep[top[v]])
            {
                Add(root, pos[top[u]], pos[u], 1, n, x);
                u = f[top[u]];
            }
            else
            {
                Add(root, pos[top[v]], pos[v], 1, n, x);
                v = f[top[v]];
            }
        }
        if(dep[u] < dep[v])
            Add(root, pos[u], pos[v], 1, n, x);
        else
            Add(root, pos[v], pos[u], 1, n, x);
    }
    
    void work2(int u, int v, int x)
    {
        while(top[u] != top[v])
        {
            if(dep[top[u]] > dep[top[v]])
            {
                Cover(root, pos[top[u]], pos[u], 1, n, x);
                u = f[top[u]];
            }
            else
            {
                Cover(root, pos[top[v]], pos[v], 1, n, x);
                v = f[top[v]];
            }
        }
        if(dep[u] < dep[v])
            Cover(root, pos[u], pos[v], 1, n, x);
        else
            Cover(root, pos[v], pos[u], 1, n, x);
    }
    
    int work3(int u, int v)
    {
        int ret = 0;
        while(top[u] != top[v])
        {
            if(dep[top[u]] > dep[top[v]])
            {
                ret += Query(root, pos[top[u]], pos[u], 1, n);
                u = f[top[u]];
            }
            else
            {
                ret += Query(root, pos[top[v]], pos[v], 1, n);
                v = f[top[v]];
            }
        }
        if(dep[u] < dep[v])
            ret += Query(root, pos[u], pos[v], 1, n);
        else
            ret += Query(root, pos[v], pos[u], 1, n);
        return ret;
    }
    
    bool check(int x)
    {
        if(x <= 3)    return 0;
        if(!(x&1))    return 1;
        x -= 2;
        return !prime[x];
    }
    
    int main()
    {
        freopen("japari.in", "r", stdin);
        freopen("japari.out", "w", stdout);
        Euler();
        read(n);read(m);
        for(int i = 1; i <= n; ++i)
            read(a[i]);
        for(int i = 1; i < n; ++i)
        {
            int x, y;
            read(x);read(y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        dfs1(1);
        //cout<<"hahahahaha"<<endl;
        dfs2(1);
        build(root, 1, n);
        for(int i = 1; i <= m; ++i)
        {
            int opt, u, v;
            read(opt);read(u);read(v);
            if(opt == 1)
            {
                int x;
                read(x);
                work1(u, v, x);
            }
            else if(opt == 2)
            {
                int x;
                read(x);
                work2(u, v, x);
            }
            else
            {
                int res = work3(u, v);
                printf("%s
    ", check(res)? "SUGOI": "TANOSHI");
            }
            
        }
        return 0;
    }
    japari

    T2:

      

    黑白熊的密码

    256MB / 1s ; monokuma.cpp / c / pas / in / out

    【题目描述】

    苗木诚发现了脱出装置,它可以帮助所有人逃脱希望之峰学园。但是要想使用它,必须输入一个十进制的n位数密码(没有前导0)。为了让游戏变得更加有趣,黑白熊提供了m条信息,其中第i条信息指出这个数的第Li位到Ri位和第li位到ri位完全相同。

    苗木诚决定随机选择一个满足要求的n位数输入。请你告诉他他碰对密码的概率是多少。

     

    【输入格式】

    第一行两个整数n,m,表示数的位数和黑白熊提供的信息数。

    接下来m行,每行四个正整数Li,Ri,li,ri。

     

    【输出格式】

    输出一行一个整数,表示苗木诚碰对密码的概率,对998244353取模。

     

    【样例数据】

    monokuma.in

    monokuma.out

    4 2

    1 2 3 4

    3 3 3 3

    144190851

     

    【样例解释】

    答案是1/90在模998244353意义下的值。

     

    【数据范围】

    对于30%的数据,n,m≤2000。

    对于100%的数据,1≤n,m≤100000,1≤Li≤Ri≤n,1≤li≤ri≤n,且保证Ri-Li=ri-li。

    解析:

      原题:Bzoj4569: [Scoi2016]萌萌哒

      并查集好题, 考试的时候我只写了暴力维护每个点的并查集的算法, 30pts。1e5的数据范围显然需要一个log的优化, 以方便快速地维护并查集,发现在单点维护并查集时,许多点都被访问了多次,这样很不优,于是在并查集中维护连续的点,即一段区间,在最后把标记下放给小一级的区间,最终可以下放到单点, 区间长度与在st表中一样, 都是2k,这样就做到了O(nlogn)的时间复杂度

     代价:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 100005;
    const ll mod = 998244353;
    
    template<class T>
    inline void read(T &re)
    {
        T f=1;char c;
        while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1;
        re=c-'0';
        while((c=getchar())&&c>='0'&&c<='9')re = (re<<3) + (re<<1) + c - '0';
        re*=f; 
    }
    
    int n, m, tot, f[maxn*20], res, pw[20], timer, mp[maxn*20], id[20][maxn];
    ll ans = 1;
    
    ll qpow(ll x, ll y)
    {
        ll ret = 1;
        while(y)
        {
            if(y&1)
                ret = ret * x % mod;
            x = x * x % mod;
            y>>=1;
        }
        return ret;
    }
    
    int Find(int x)
    {
        return x == f[x]? x: f[x] = Find(f[x]);
    }
    
    int main()
    {
        freopen("monokuma.in", "r", stdin);
        freopen("monokuma.out", "w", stdout);
        read(n);read(m);
        pw[0] = 1;
        for(int i = 1; i <= 18; ++i)
            pw[i] = pw[i-1] * 2;
        for(int i = 1; i <= n; ++i)
            for(int j = 0; j <= 18 && i + pw[j] - 1 <= n; ++j)
            {
                id[j][i] = ++timer;
                mp[timer] = i;
                f[timer] = timer;
            }
        for(int i = 1; i <= m; ++i)
        {
            int l1, r1, l2, r2;
            read(l1);read(r1);read(l2);read(r2);
            for(int j = 18; j >= 0; --j)
                if(l1 + pw[j] - 1 <= r1)
                {
                    f[Find(id[j][l1])] = Find(id[j][l2]);
                    l1 += pw[j];
                    l2 += pw[j];
                }
        }
        for(int j = 18; j; --j)
            for(int i = 1; i + pw[j] - 1 <= n; ++i)
            {
                int k = Find(id[j][i]), s = mp[k];
                f[Find(id[j-1][i])] = Find(id[j-1][s]);
                f[Find(id[j-1][i+pw[j-1]])] = Find(id[j-1][s+pw[j-1]]);
            }
        for(int i = 1; i <= n; ++i)
            if(Find(id[0][i]) == id[0][i])
                res++;
        ans = 1LL * 9;
        ans = ans * qpow(1LL * 10, 1LL * (res - 1)) % mod;
        printf("%lld", qpow(ans, mod - 2));
        return 0;
    }
    monokuma

    T3:

    初音未来的巡游

    128MB / 1s ; cruise.cpp / c / pas / in / out

    【题目描述】

    Miku决定在n个地点中选择一些地点进行巡游。这n个地点由n-1条道路连接,两两之间有且仅有一条路径可以互相到达。Miku希望在这些道路中选择一些放上葱,使得Miku可以选择一种方式只经过有葱的道路而巡游完所有她选择的地点(一条道路可以被多次经过,起点任选)。

    Miku想知道她至少需要准备多少葱。由于她的巡游计划可能更改,她希望你能在更改后即时回答她的疑问。

     

    【输入格式】

    第一行两个整数n,m,表示地点数和事件数。

    2n行,每行两个整数x,y,表示地点x和地点y之间有一条无向道路。

    接下来一行n0/1数,若第i个数为1则表示i号地点初始时被选,为0则表示不选。

    接下来一行m个整数,依次表示修改事件。第i个数Ai表示Ai号地点的状态发生变化,即若当前被选则改为不选,当前不选则改为被选。

     

    【输出格式】

    输出m行,第i行一个整数表示第i次事件后初音最少需要准备多少葱。

     

    【样例数据】

    cruise.in

    cruise.out

    5 8

    1 2

    1 3

    2 4

    2 5

    1 0 0 1 0

    5 4 2 1 2 5 3 2

    3

    2

    2

    1

    0

    0

    0

    2

     

    【数据范围】

    对于30%的数据,n,m3000对于另30%的数据,开始时所有地点都不选,保证修改操作的地点当前是不选状态。

    对于100%的数据,1n,m2000001x,y,Ain

     

     

    解析:

       没搞出来, 先CV一波Solution

    可以证明答案的两倍为按DFS序排序后相邻两个选择点的距离和加上首尾点的距离,则预处理LCA,使用set维护选择的点,进行修改时根据DFS序的前驱和后继点更新答案即可。

    复杂度O(mlogn + nlogn)。

     代码鸽了

  • 相关阅读:
    牛客小白月赛3 I 排名【结构体排序/较复杂/细节】
    牛客小白月赛3 F 异或【区间交集】
    牛客小白月赛3 B 躲藏【动态规划/字符串出现cwbc子序列多少次】
    陕西师范大学第七届程序设计竞赛网络同步赛 I 排队排队排队【数组任一位可以移动到队头,最少移动几次增序/数组指针操作】
    陕西师范大学第七届程序设计竞赛网络同步赛 J 黑猫的小老弟【数论/法拉数列/欧拉函数】
    陕西师范大学第七届程序设计竞赛网络同步赛D ZQ的睡前故事【约瑟夫环1-N数到第k个出队,输出出队顺序/ STL模拟】
    陕西师范大学第七届程序设计竞赛网络同步赛 C iko和她的糖【贪心/ STL-优先队列/ 从1-N每个点有能量补充,每段有消耗,选三个点剩下最多能量】
    陕西师范大学第七届程序设计竞赛网络同步赛 F WWX的礼物【数学/k进制下x^n的位数/log】
    NYOJ 71 乘船问题【贪心】
    hdu 5273 Dylans loves sequence
  • 原文地址:https://www.cnblogs.com/Joker-Yza/p/11253108.html
Copyright © 2020-2023  润新知