黄金拼图

题目描述

九条可怜有 n 盒拼图,每盒拼图都有若干拼图块,可以拼出许多矩形图案。

可是,可怜经常会弄丢拼图块,因此她需要将一些拼图送回厂家进行补块。可怜懒得将所有拼图拼好来检查完整性,仅当她的一盒拼图的拼图块数无法组成任何r 块×c 块的矩形图案(其中 r,c≥2),可怜才认为这盒拼图需要返厂补块。返厂补块需要的运费只和含有图块数最多的拼图有关。

可怜将 n 盒拼图从 1 到 n 编号。每次,可怜都想知道,如果从编号在[l,r] 区间内的拼图中选择 k 盒一定需要补块的拼图,拼图块数最多的拼图的拼图块数最少是多少。当然,可怜只是随口问问,并不会真的将这些拼图返厂,所以询问后所有拼图的块数都不会变化。
有时,可怜会发现自己数错了拼图的块数,并将第 x 盒拼图的拼图块数更新为 y。她希望你能即时回答这些询问。

输入格式

第一行两个整数 n,k,m,m 表示可怜询问和修改的数量和。

接下来一行 n 个正整数,第 i 个数表示第 i 盒拼图初始时的拼图块数。

接下来 m 行,每行 3 个数 opt,l,r。为了表明你在即时回答可怜的询问,真实的 opt,l,r 为输入的 opt,l,r 分别异或(XOR)lastans,其中 lastans 表示上一次询问的答案,若之前没有询问操作,则 lastans=0。

若 opt=1,则表示这是一次询问操作,询问的区间为[l,r]。

若 opt=2,则表示这是一次修改操作,把第 l 盒拼图的块数修改为 r。

输出格式

对于每个询问操作,输出一行一个整数,表示拼图块数最多的拼图的拼图块

数最少是多少。保证答案存在。

样例数据

3 1 3             5
4 5 6             7
1 1 3
7 7 2
4 4 6

3 2 3             7
4 5 7             5
1 1 3
5 4 2
6 6 4

数据范围

对于 30%的数据,n,m≤1000。
对于另 30%的数据,没有修改操作。
对于 100%的数据,1≤n,m,k≤200000,l,r 合法,所有拼图的块数在任何时刻是[4,1000000]区间中的整数。

扫描二维码关注公众号,回复: 7088014 查看本文章

如果对于 \(n\) ,有 \(n=r*c\),那么 \(n\) 就不需要返厂,也就说需要返厂的只有素数。
对于 \(opt=1\) ,需要在区间内选择 \(k\) 个数,使得最大的最小,返回最大的那个数。显然选择 \(k\) 个最小的数最优,于是问题变成了查询区间质数第 \(k\) 大,可以用主席树。
对于 \(opt=1\),单点修改,再结合1操作,想到树套树。

然而不管多么优秀的树套树都只能得到60分,数据还是太大?那么最奇妙的来了,我们发现不仅连操作的值被异或了(强制在线),opt也异或了?!opt只有1和2,一奇一偶。考虑对opt进行分类讨论,首先答案的值只可能是奇数(保证为[4,1000000]的素数),如果opt是奇数,又有 \((原opt)^lastans=现opt\),所以原opt一定是偶数,也就是2,同理如果opt为偶,那么原opt=1,最后把现opt异或上原opt就得到上一次操作的答案(???!!!)

Code:

#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 1000003
#define INF 0x3f3f3f3f

int n,m,a[N],b[N],p[N],k,p_num;
bool mark[N];

template<class T>
inline void read(T &x){
    x=0;char c=getchar();T flag=1;
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    x*=flag;
}

inline void Pre(){
    mark[1]=1;
    for(int i=2;i<N;i++){
        if(!mark[i])
            p[++p_num]=i;
        for(int j=1;j<=p_num&&i*p[j]<N;j++){
            mark[i*p[j]]=1;
            if(i%p[j]==0) break;
        }
    }
}
int main(){
    freopen("jigsaw.in","r",stdin);
    freopen("jigsaw.out","w",stdout);
    Pre();
    read(n),read(k),read(m);
    int opt,l,r,ans=0;
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=m;i++){
        read(opt),read(l),read(r);
        if(opt&1){
            l^=opt^2,r^=opt^2; 
            a[l]=r;
            continue;
        }
        ans=opt^1;
        if(ans!=3&&i!=1)
            printf("%d\n",ans); 
    }
    if(opt&1) return 0;
    l^=opt^1,r^=opt^1; 
    for(int i=l;i<=r;i++)
        if(!mark[a[i]])
            b[++b[0]]=a[i];
    sort(b+1,b+b[0]+1);
    printf("%d\n",b[k]);
}

猜你喜欢

转载自www.cnblogs.com/wwlwQWQ/p/11405039.html