1.9 czk

1

给定一个序列,支持两种操作(允许离线):
1.插入一个数
2.给出 l , r l,r l,r ,求最大的 k k k 使得 1 … k 1\dots k 1k [ l , r ] [l,r] [l,r] 中都出现过。

i i i 棵主席树的第 j j j 个叶子维护 a [ i ] … a [ n ] a[i]\dots a[n] a[i]a[n] j j j 这个数上一次出现位置的 min ⁡ \min min。对于一个询问 [ l , r ] [l,r] [l,r],我们找到第 r + 1 r+1 r+1 棵主席树在里面二分最小的一个数使得上一次出现在 l l l 之前即可。要做带插入的话离线带修主席树即可。

2

CF800D
定义一个整数集合的 F F F 值为每一位取 min ⁡ \min min 得到的数。对于一个序列, G ( x ) G(x) G(x) 表示所有 F ( S ) = x F(S)=x F(S)=x 的集合的数的和的平方和。给定序列 a a a,求所有 G ( x ) G(x) G(x)

g 2 [ x ] g2[x] g2[x] 表示 F ( S ) F(S) F(S) 每一位都大于等于x的S的权值和的平方和, g 0 , g 1 g0,g1 g0g1 同理。我们做一个高维前缀和,类似 FMT, h [ i ] [ x ] h[i][x] h[i][x] 表示前 i i i 位与 x x x 相同,后面是 x x x 的超集的那些数,每个子集的权值和的平方和。合并的时候要用零次方、一次方、二次方合并。

这样我们能够得出 H ( x ) H(x) H(x) 表示所有 F ( S ) F(S) F(S) x x x 超集的集合的数的和的平方和。

要求出答案,只需要做一个简单的容斥。类似 FMT 的逆变换。

(对 FMT 的理解加深了)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
struct node{
    ll f0,f1,f2;
}f[7][1000010];
ll mi[1000010],a[1000010];
inline int read()
{
    char c=getchar();int x=0,flag=1;
    while(!isdigit(c)){if(c=='-') flag=-1;c=getchar();}
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
    return x*flag;
}
node merge(node a,node b)
{
    node tmp;
    tmp.f0=(a.f0+b.f0+a.f0*b.f0)%mod;
    tmp.f1=(a.f1+b.f1+a.f1*b.f0+b.f1*a.f0)%mod;
    tmp.f2=(a.f2+b.f2+2ll*a.f1*b.f1+a.f2*b.f0+b.f2*a.f0)%mod;
    return tmp;
}
int main()
{
    int n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        f[0][a[i]]=merge(f[0][a[i]],(node){1,a[i],1ll*a[i]*a[i]});
    }
    mi[0]=1;
    for(int i=1;i<=6;i++) mi[i]=mi[i-1]*10;
    for(int i=1;i<=6;i++)
    {
        for(int j=999999;j>=0;j--)
        {
            int num=(j/mi[i-1])%10;
            if(num<9) f[0][j]=merge(f[0][j],f[0][j+mi[i-1]]);
        }
    }
    for(int i=1;i<=6;i++)
    {
        for(int j=999999;j>=0;j--)
        {
            int num=(j/mi[i-1])%10;
            f[i][j]=f[i-1][j];
            if(num<9) f[i][j].f2-=f[i-1][j+mi[i-1]].f2;
            if(f[i][j].f2<0) f[i][j].f2+=mod;
        }
    }
    ll ans=0;
    for(int i=0;i<1000000;i++) ans^=(f[6][i].f2%mod*i);
    cout<<ans;
    return 0;
}

3

给定一棵树,每个点有一种颜色,每次询问一棵子树里深度大于等于某个数的点的颜色集合(相同颜色算一遍)中编号第 k k k 小的颜色是什么。

看起来就很线段树合并。但是好像多了一维。因此我们整体二分(谁说整体二分没用的)。

二分一个颜色,看那些询问的答案小于这个颜色。对于二分的某一层,把询问的颜色和询问点拿出来建虚树。再把每种颜色建虚树,在度数大于 1 的节点处只保留深度最浅的那个点的信息。这样保证了一种颜色被算一次。然后在所有颜色的虚树上线段树合并即可。查询区间和是否大于 k k k。若大于,递归左边,否则把 k k k 减掉颜色个数递归到右边。

4

n n n 个数,求一个最大的 k k k,使得能选出 k k k 个数模某个数 m ( m &gt; 1 ) m(m&gt;1) m(m>1) 相等。在 k k k 最大的条件下最大化 m m m

容易发现 k ≥ n / 2 k\geq n/2 kn/2。因此,我们随机选一个数,它在答案里的概率至少为 1 / 2 1/2 1/2。每次 rand 一个数,可以算出包含这个数的答案。方法是每个数对它求差作为 c i c_i ci,找一个最大的 d d d 使得 d ∣ c i d|c_i dci 的数量最多。

5

给定一个颜色序列,支持两种操作:
1.把所有颜色 x x x 修改为 y y y
2.询问 x , y x,y x,y,回答最小的区间长度使得这个区间里同时存在 x , y x,y x,y

看上去就是分块好题。

对于出现次数小于 n \sqrt n n 的颜色,暴力记下位置。对于出现次数大于 n \sqrt n n 的颜色,记录它与每个颜色的答案。这样可以 O ( n n ) O(n\sqrt n) O(nn ) 回答询问。

对于修改,我们只需要每种颜色开一个附属集合,把 x x x 修改为 y y y,就在 y y y 的附属集合里加入 x x x。如果附属集合大于 n \sqrt n n 就暴力重构。显然重构次数不回超过 n \sqrt n n 次。查询的时候一种颜色实际上是它和附属集合的并。分类讨论一下即可。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值