国庆suprise

T1[数论+裴蜀定理]给出[a,b],可以对序列进行2种操作,(a-b,b * 2)/(a * 2,b-a)(mod p意义下)。求把二元组变成[c,d]最少变换次数。a,b<=1e9+7

发现操作实际意义,a和b的系数是不变的,而且系数绝对值是操作次数的2次幂。设k次操作之后a的系数是x
\([x*a-(2^{k}-x)*b,(1-x)*a+(2^k-x+1)*b]\)就是操作后的数对,因为[a,b]本质不变,所以可以知道[a,b]%p=[c,d]%p,否则不合法。
把[X,Y]同余[c,d]列出来,我们想要求出x的对应限制,因为枚举k再枚举x不现实,
发现x同余\((c-d+2^{k+1}*b+c+d ) / 2*(a+b)\)(%p)
这是合法的情况。所以我们枚举k,只要能求出x在[1,2 ^ k]内的一个解,说明k'次操作合法。当2^k>=p一定合法,因为无论x是多少一定在p内,所以一定可以落在合法区间,这时如果x=0代表=p,也合法。

点击查看代码






#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define rint register int
#define ll long long
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
#define pii pair<ll,ll>
map<pii,ll>mp;
deque<pii>q;
ll mod,qq,a,b,c,d,cf2[40],_cf2[40];
inline ll qpow(ll x,ll y)
{
    ll  op=1;
    while(y)
    {
        if(y&1)op=op*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return op;
}
inline void deal_()
{
    _f(i,1,qq)
   {
       ll a=re(),b=re(),c=re(),d=re();
       if((a+b)%mod==(c+d)%mod)//这样才会一定有解
       {
           //开始找解
          // chu("in;%lld %lld\n",a,b);
           mp.clear();q.clear();
           mp[make_pair(a,b)]=0;//需要多少回凑出来
           q.push_front(make_pair(a,b));
          ll ans=0;
           while(!q.empty())
           {
                pii tmp=q.front();q.pop_front();
                if(tmp.first==c&&tmp.second==d)
                {
                    ans=mp[tmp];break;
                }
               // chu("here:%lld %lld %lld\n",tmp.first,tmp.second,mp[tmp]);
               ll a1=tmp.first*2%mod,a2=(tmp.second-tmp.first+mod)%mod;
                if(mp.find(make_pair(a1,a2))==mp.end())
                q.push_back(make_pair(a1,a2)),
                mp[make_pair(a1,a2)]=mp[tmp]+1;
                a1=(tmp.first-tmp.second+mod)%mod,a2=tmp.second*2%mod;
                if(mp.find(make_pair(a1,a2))==mp.end())
                q.push_back(make_pair(a1,a2)),
                mp[make_pair(a1,a2)]=mp[tmp]+1;
           }
           chu("%lld\n",ans);
       }
       else
       {
           chu("-1\n");
       }
   }
}
int main()
{
  // freopen("rubyonly.in","r",stdin);
  //freopen("1.out","w",stdout);
  mod=re(),qq=re();
  if(qq<=3000)
  {
      deal_();return 0;
  }
  ll bj=log2(mod)+1;
 // chu("bj:%lld\n",bj);
  cf2[0]=1;bool chao=0;
  _cf2[0]=1;
  _f(i,1,30)
  {
      if(!chao)
      {
          cf2[i]=cf2[i-1]*2;
          if(cf2[i]>=mod)chao=1;
      }
      else cf2[i]=mod;
      _cf2[i]=_cf2[i-1]*2%mod;
  }
  _f(i,1,qq)
  {
     // chu("\n\nnew\n");
     a=re(),b=re(),c=re(),d=re();
     if((a+b)%mod!=(c+d)%mod)
     {
         chu("-1\n");continue;
     }    
     if(a==c&&b==d)
     {
         chu("0\n");continue;
     } 
     ll ny=qpow(a+b,mod-2);
     _f(k,1,bj)//枚举边界,指数
     {
         ll my=(c+_cf2[k]*b%mod)%mod*ny%mod;
         //chu("now my:%lld  cf2[%d]:%lld\n",my,k,cf2[k]);
         if(my<=cf2[k]&&my>=1)
         {
             chu("%d\n",k);break;
         }
     }
  }
    return 0;
}
/*
5 10
2 1 3 0
2 1 4 4
1 3 4 0
0 2 0 4
3 3 1 2
0 1 0 1
0 3 0 3
0 1 0 1
1 2 4 4
1 0 1 1

2
1
2
-1
-1
0
0
0
1
-1
*/

T2[数据结构]给出颜色序列,q次询问[l,r]区间内颜色出现次数>=K的颜色数量,强制在线。(n,q<=5e5)

对于离线

出现次数就是值域分块。把值域按照sqrt分块,维护cnt[x]表示出现次数在x块对应区间的颜色个数,buc[x]维护散块出现次数是x的数个数,因为整块<=sqrt,散块大小<=sqrt所以复杂度对。查询就莫队移动,insert和delete对应更新buc和cnt。tot记录总的颜色出现个数。

点击查看代码


/*

出现次数值域分块

*/
#include <bits/stdc++.h>
using namespace std;
#ifdef ONLINE_JUDGE
    char buf[1<<21], *p1 = buf, *p2 = buf;  inline char getc() { return (p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++); }
    #define getchar getc
#endif
template <typename T> inline void get(T & x){
	x = 0; char ch = getchar(); bool f = 0; while (ch < '0' or ch > '9') f = f or ch == '-',  ch = getchar();
	while (ch >= '0' and ch <= '9') x = (x<<1) + (x<<3) + (ch^48), ch = getchar(); f and (x = -x);
} template <typename T, typename ... Args> inline void get(T & x, Args & ... _Args) { get(x); get(_Args...); }
#define rep(i,a,b) for (register int (i) = (a); (i) <= (b); ++(i))
#define pre(i,a,b) for (register int (i) = (a); (i) >= (b); --(i))
const int N = 1e5 + 10;
int n, a[N], q, k, ans[N], opt, buk[N], siz, sn, bl[N], lp[N], rp[N];

struct queries {
    int l, r, k, id;
    bool operator < (const queries & b) const {
        if (bl[l] != bl[b.l]) return l < b.l;
        if (bl[l] & 1) return r < b.r;
        return r > b.r;
    }
} qr[N];

struct BLO {
    int tot, cntpts[N], cntblk[N];
    
    void add(int pos, int v) {
        cntpts[pos] += v;
        cntblk[bl[pos]] += v;
        tot += v;
    }

    int qry(int k) {
        if (k == 1) return tot;
        k--; 
        int bl_k = bl[k], ret = 0;
        rep(i,1,bl_k-1) ret += cntblk[i];
        rep(i,lp[bl_k],k) ret += cntpts[i];
        return tot - ret;
    }
} b;

signed main() {
    get(n, q, opt); siz = sqrt(n);
    rep(i,1,n) get(a[i]);

    lp[1] = 1;
    rep(i,1,n) {
        bl[i] = (i-1) / siz + 1;
        if (bl[i] != bl[i-1]) lp[bl[i]] = i, rp[bl[i-1]] = i-1;
    } 
    sn = bl[n], rp[sn] = n;

    if (opt == 0) {
        rep(i,1,q) {
            get(qr[i].l, qr[i].r, qr[i].k), qr[i].id = i;
            int mn = min((qr[i].l - 1) % n + 1, (qr[i].r - 1) % n + 1), mx = max((qr[i].l - 1) % n + 1, (qr[i].r - 1) % n + 1);
            qr[i].l = mn;
            qr[i].r = mx;
            qr[i].k = (qr[i].k - 1) % n + 1;
        }
        sort(qr+1, qr+1+q);
        for (register int i = 1, l = 1, r = 0; i <= q; i++) {
            while (l > qr[i].l) {
                -- l;
                if (buk[a[l]] != 0) b.add(buk[a[l]], -1);
                buk[a[l]]++;
                b.add(buk[a[l]], 1);
            }
            while (r < qr[i].r) {
                ++ r;
                if (buk[a[r]] != 0) b.add(buk[a[r]], -1);
                buk[a[r]]++;
                b.add(buk[a[r]], 1);
            }
            while (r > qr[i].r) {
                b.add(buk[a[r]], -1);
                buk[a[r]]--;
                if (buk[a[r]] != 0) b.add(buk[a[r]], 1);
                -- r;
            }
            while (l < qr[i].l) {
                b.add(buk[a[l]], -1);
                buk[a[l]]--;
                if (buk[a[l]] != 0) b.add(buk[a[l]], 1);
                ++ l;
            }
            ans[qr[i].id] = b.qry(qr[i].k);
        } rep(i,1,q) cout << ans[i] << '\n';
    }
}

对于在线

出现次数>sqrt的个数<sqrt,出现次数<sqrt的我们可以统计个缀直接块暴力。
具体的,维护
cor[i][j]:表示在1~i块中出现次数是j的总和
pol[i][j][k]表示(i,j)块中出现次数在k~S的后缀和
chac[i][j]:维护在i~j块中的出现次数>S的颜色是谁。
询问的时候,如果K<=S,那么chac的大小直接加上整块的,对于pol也加上后缀,在散块加的时候为了不重复限制次数是恰好。
如果K>S,直接在chac里面暴力硬扫出块中cnt>K的就可以
如果不单独分出S,pol会炸。

点击查看代码


#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int M=320,N=1e5+100;
int cor[M][N],buc[N],tot,pol[M][M][M],boun[M],S,Code,a[N],n,opt,q,belon[N];
int rem[N];
vector<int>chac[M][M];
int main()
{
   //freopen("suzipei.in","r",stdin);
  // freopen("1.out","w",stdout);
    n=re(),q=re(),opt=re();S=sqrt(n);Code=n/S;
    _f(i,1,n)a[i]=re();
    _f(i,1,Code)boun[i]=S*i;
    boun[Code]=n;
    _f(i,1,Code)//块,然后长度
    {
        _f(j,boun[i-1]+1,boun[i])
        cor[i][a[j]]++,belon[j]=i;
        _f(j,1,n)cor[i][j]+=cor[i-1][j];
    }
    _f(i,1,Code)
    {
        fill(buc+1,buc+1+n,0);
        _f(j,i,Code)
        {
            _f(k,1,S)pol[i][j][k]=pol[i][j-1][k];//出现次数是k的个数
            //把它加进去
            chac[i][j]=chac[i][j-1];
            _f(k,boun[j-1]+1,boun[j])
            {
                if(buc[a[k]]==S)
                {
                    pol[i][j][buc[a[k]]++]--;
                    chac[i][j].push_back(a[k]);//颜色放进去,就行了吧?
                }
                else if(buc[a[k]]<S)
                {
                    pol[i][j][buc[a[k]]++]--;
                    pol[i][j][buc[a[k]]]++;
                }
            }
        }
    }
    _f(i,1,Code)
    _f(j,1,Code)
    f_(k,S,1)
    pol[i][j][k]+=pol[i][j][k+1];
//然后查询就行,pol是次数的前缀和(k就是),cor是1~i块中某种颜色的出现次数
    int ans=0;int l=0,r=0,lim=0;
    memset(buc,0,sizeof(buc));
    // for(int i=1;i<=Code;++i)
    // {
    //     for(int j=1;j<=Code;++j)
    //     {
    //         for(int k=1;k<=S;++k)
    //         printf("%d ",pol[i][j][k]);
    //         printf("\n");
    //     }
    //     printf("\n");
    // }
    _f(i,1,q)
    {
        l=re(),r=re(),lim=re();
        int x=min((l+ans*opt-1)%n+1,(r+ans*opt-1)%n+1);
        int y=max((l+ans*opt-1)%n+1,(r+ans*opt-1)%n+1);
        l=min(x,y);r=max(x,y);
        lim=(lim+ans*opt-1)%n+1;
        ans=0;
        int lc=belon[l],rc=belon[r];
         //chu("Ask:%d %d %d %d %d %d\n",l,r,lim,lc,rc,S);
        if(lc==rc)//一个块直接求解
        {
          //  chu("here\n");
            _f(j,1,tot)buc[rem[j]]=0;tot=0;
            _f(j,l,r)
            {
             //   chu("buc[%d]:%d\n",a[j],buc[a[j]]);
                buc[a[j]]++;
                if(buc[a[j]]==lim)++ans;
                if(buc[a[j]]==1)rem[++tot]=a[j];
            }
        }
        else
        {
            if(lim>S)//先处理整块,如果限制本身大,直接从处理好的里面找符合要求的,一定在chac里面
            {
                for(rint ele:chac[lc+1][rc-1])
                {
                    if(cor[rc-1][ele]-cor[lc][ele]>=lim)ans++;
                }
            }
            else//否则直接累加上
            {
                ans=chac[lc+1][rc-1].size()+pol[lc+1][rc-1][lim];
            }
            //开始散块的
         //   chu("nowans:%d\n",ans);
            _f(j,1,tot)buc[rem[j]]=0;tot=0;
            _f(j,l,boun[lc])
            {
               // chu("insert:%d\n",a[j]);
                buc[a[j]]++;
                if(buc[a[j]]==1)rem[++tot]=a[j];
                if(buc[a[j]]+cor[rc-1][a[j]]-cor[lc][a[j]]==lim)ans++;//chu("has\n");//这里还需要
            }
            _f(j,boun[rc-1]+1,r)
            {
              //  chu("you insert:%d\n",a[j]);
                buc[a[j]]++;
                if(buc[a[j]]==1)rem[++tot]=a[j];
                if(buc[a[j]]+cor[rc-1][a[j]]-cor[lc][a[j]]==lim)++ans;//chu("yes\n");
            }
        }
        chu("%d\n",ans);
    }
    return 0;
}
/*
10 3 0
3 4 3 2 4 1 2 1 3 1 
2 1 1
4 6 1
1 5 2
*/

T3[Mex]询问【1】多段[l,r]区间的mex【2】整个区间mex的mex

对于多段,线段树直接维护;
对于整个区间,我们考虑怎么算出一个x可不可以成为mex:last_mexmex的位置出现了1mex-1的数,也就是1~mex-1的数最近出现位置>last_mex,线段树维护

点击查看代码


#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=1e6+100;
int Mi[N<<2],tag[N<<2];
int n,a[N],lst[N],can[N];//维护目前每个值出现最晚位置,区间最小值,如果没出现?min>lst[pos],就是不合法,所以应该赋值-无穷
int ls[N<<2],rs[N<<2],tot,root;
struct Que
{
    int l,r,id;
    bool operator<(const Que&U)const
    {
        return l<U.l;
    }
}e[N];
inline void Update(int&rt,int l,int r,int pos,int val)
{
    if(!rt)rt=++tot;
    if(l==r)
    {
        Mi[rt]=val;//一定是更新
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)Update(ls[rt],l,mid,pos,val);
    else Update(rs[rt],mid+1,r,pos,val);
    Mi[rt]=min(Mi[ls[rt]],Mi[rs[rt]]);
    // else if(ls[rt])Mi[rt]=Mi[ls[rt]];
    // else if(rs[rt])Mi[rt]=Mi[rs[rt]];
}
inline int Query(int rt,int l,int r,int L,int R)
{
    //if(R<L)return 1e9;
    //if(!R)return 1e9;
   // chu("now find:%d--%d\n",l,r);
    if(!rt)return -1e9;
    if(L<=l&&r<=R)return Mi[rt];
    int mid=(l+r)>>1;int ans=1e9;//取最小值
    if(L<=mid)ans=Query(ls[rt],l,mid,L,R);
    if(R>mid)ans=min(ans,Query(rs[rt],mid+1,r,L,R));
    return ans;
}
#define lson (rt<<1)
#define rson  (rt<<1|1)
//支持单点查询和区间修改
inline void _build(int rt,int l,int r)
{
    if(l==r)
    {
        Mi[rt]=lst[l];return;
    }
    int mid=(l+r)>>1;
    _build(lson,l,mid);
    _build(rson,mid+1,r);
    //wait? I need?只需要单点查询...
    Mi[rt]=min(Mi[lson],Mi[rson]);
}
inline void _pushdown(int rt)
{
    if(tag[rt])
    {
        Mi[lson]=Mi[rson]=tag[lson]=tag[rson]=tag[rt];
        tag[rt]=0;
    }
}
inline void _pushup(int rt)
{
    Mi[rt]=min(Mi[lson],Mi[rson]);
}
inline void _update(int rt,int l,int r,int L,int R,int val)
{
    if(L<=l&&r<=R)
    {
        Mi[rt]=tag[rt]=val;return;
    }
    int mid=(l+r)>>1;
    _pushdown(rt);
    if(L<=mid)_update(lson,l,mid,L,R,val);
    if(R>mid)_update(rson,mid+1,r,L,R,val);
    _pushup(rt);
}
inline int _query(int rt,int l,int r,int pos)
{
    if(l==r)
    {
        return Mi[rt];
    }
    _pushdown(rt);
    int mid=(l+r)>>1;
    if(pos<=mid)return _query(lson,l,mid,pos);
    else return _query(rson,mid+1,r,pos);
}
int main()
{
 //  freopen("kaiser_kell.in","r",stdin);
  // freopen("1.out","w",stdout);
    n=re();
    _f(i,1,n)a[i]=re();
    _f(i,0,(n<<2))Mi[i]=-1e9;
    _f(i,1,n)
    {
        Update(root,1,n,a[i],i);
        if(can[a[i]])
        {
            lst[a[i]]=i;continue;
        }
        if(a[i]==1)
        {
            if(lst[a[i]]==i-1&&i!=1){}
            else can[1]=1;
        }
        else
        {
            int Minp=Query(root,1,n,1,a[i]-1);
            if(Minp>lst[a[i]])can[a[i]]=1;
        }
        lst[a[i]]=i;
    }
    _f(i,1,n)//按照值最后check一遍
    {
        if(can[i])continue;
        int Minp=Query(root,1,n,1,i-1);
        if(Minp>lst[i])can[i]=1;
    }
    if(Query(root,1,n,1,n)>0)//都出现过
    can[n+1]=1;
    int mex=1;
    while(can[mex])++mex;
    chu("%d\n",mex);
    int Mex=1;
    fill(lst+1,lst+1+n+1,0);
    fill(can+1,can+1+n+1,0);
    fill(Mi+1,Mi+1+(n<<2),0);
    fill(ls+1,ls+2+n,0);
    fill(rs+1,rs+2+n,0);
    _f(i,1,n)
    {
        can[a[i]]++;
        while(can[Mex])Mex++;
        lst[i]=Mex;//lst存区间mex
        rs[i]=n+1;//rs存值nxt
    }
    f_(i,n,1)
    {
        ls[i]=rs[a[i]];
        rs[a[i]]=i;
    }
    //ls维护位置下一个出现位置
    _build(1,1,n);
   // chu("out\n");
    int Q=re();
    _f(i,1,Q){
        e[i].l=re(),e[i].r=re();e[i].id=i;
    }
    sort(e+1,e+1+Q);
    e[0].l=1;
    _f(i,1,Q)
    {
        if(e[i].l!=e[i-1].l)
        {
            _f(j,e[i-1].l,e[i].l-1)//需要删除的数
            {
               // chu("del:%d\n",j);
                int l=j+1,r=ls[j]-1;//这些区间里找
                if(l>r)continue;//没有影响
                int ans=r+1;
                while(l<=r)
                {
                    int mid=(l+r)>>1;
                    //chu("query(%d):%d  a[j];%d\n",mid,_query(1,1,n,mid),a[j]);
                    if(_query(1,1,n,mid)>a[j])
                    {
                        ans=mid;r=mid-1;
                    }
                    else l=mid+1;
                }
               // chu("let %d--%d down to:%d\n",ans,ls[j]-1,a[j]);
                _update(1,1,n,ans,ls[j]-1,a[j]);
              //  chu("query(%d):%d\n",3,_query(1,1,n,3));
            }
        }
       // chu("no\n");
        can[e[i].id]=_query(1,1,n,e[i].r);
       // chu("%d--%d:ans:%d\n",e[i].l,e[i].r,can[e[i].id]);
       // chu("op\n");
    }
    _f(i,1,Q)chu("%d ",can[i]);
    return 0;
}
/*
10
1 2 3 4 5 6 7 8 9 10
5
1 6
5 8
3 3
7 10
5 9

*/
posted on 2022-10-03 21:03  HZOI-曹蓉  阅读(26)  评论(0编辑  收藏  举报