暑假D21
Posted sto324
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑假D21相关的知识,希望对你有一定的参考价值。
Jigsaw
九条可怜有n盒拼图从1开始编号,当一盒拼图的块数无法组成任何r块*c块的矩形图案时就认为它需要返厂补块。
可怜想知道编号在[l,r]内的拼图,如果选择k个一定需要补块的返厂,那么拼图块数最多的最少是多少。
有时候可怜会发现自己数错了,并将第x盒拼图块数更新成y。
对于100%的数据,1<=n,m,k<=2e5,所有拼图块数在任何时候都是[4,1e6]的整数。保证答案存在
输入格式
第一行两个整数 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。
题解
为什么这道题要把输入格式专门弄出来呢,因为这里面有很多东西。
他是强制在线。
先找思路,对于题中的返厂可以推出只要是质数就需要,要是返厂的最大的最小直接贪心,从小的开始选,第k小就是答案。
那么就是求区间第k小,可以用主席树+值域线段树解决。但是他又要修改,就GG。
所以最后只打出了暴力,当修改就清空树,当再需要询问时才建出来。
我们在读一下输入,可以发现k是固定的,这算不算很蹊跷呢?可能有,不过没发现。
OK,现在讲正解,还是从输入下手,发现这个强制在线和一般的不一样:它连opt都要异或。(这有什么特别的?)但是再从数据范围可以挖掘一个东西,那就是ans一定是奇数,因为是质数且>=4。
于是刺激的要来了,当在查询之后(有lastans时),他只有两种操作,且一个奇数一个偶数,由于lastans是奇数,所以opt=1是会读入偶数,opt=2是读入奇数。
对于这个读入我们就可以推出lastans!!!!!!!!!!
所以完整过程就是,先一个一个读入,对于修改在原序列改,当读到一个查询时停止。接着继续读入,如果opt是奇数,那么上一次查询的结果就是opt^2,不然就是opt^1。为了不重复输出,我们可以记录上次询问的答案是否得出,或者下次查询再输出也可以。
读入该异或的还是要异或,在异或时肯定通过这次询问得出了上次的答案的。
如果最后一个查询的答案没得出的话,因为我们在过程中将该修改的都改了,所以得到的就是要查询的序列,对于这个序列操作就OK。可以用桶,跑一遍就好。
树套树的空间会爆炸而且会T。有点东西的
#include<bits/stdc++.h> using namespace std; const int maxn=200005; const int maxm=9000005; int n,m,k,a[maxn]; template<class T>inline void read(T &x) x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48);ch=getchar(); const int maxk=1000000; bool not_prime[maxk+6]; int prime[maxk]; int c[maxk+6]; void pre() not_prime[1]=true; for(int i=2;i<=maxk;i++) if(!not_prime[i]) prime[++prime[0]]=i; for(int j=1;j<=prime[0]&&i*prime[j]<=maxk;j++) not_prime[i*prime[j]]=true; if(!(i%prime[j])) break; int main() freopen("jigsaw.in","r",stdin); freopen("jigsaw.out","w",stdout); pre(); read(n);read(k);read(m); for(int i=1;i<=n;i++) read(a[i]); int ans=0; int opt,l,r; while(m--) read(opt);read(l);read(r); if(opt==2) a[l]=r; else break ; bool prin=false;//当前询问是否输出 while(m--) read(opt);read(l);read(r); if(opt&1) if(!prin) printf("%d\n",ans=opt^2); prin=true; a[l^ans]=r^ans; else if(!prin) printf("%d\n",ans=opt^1); prin=false; if(!prin) l^=ans;r^=ans; for(int i=l;i<=r;i++) if(!not_prime[a[i]]) c[a[i]]++; for(int i=1;i<=prime[0];i++) if(c[prime[i]]<k) k-=c[prime[i]]; else printf("%d",prime[i]); return 0;
Baritone
有一个r*c的网格,有n个上低音号手,每个上低音号手会被安排到其中一个格子且互不相同。摄影师会拍摄照片,每个照片都是一个边框平行于坐标轴的矩形,当照片内出现至少k个上低音号手时,久美子就会喜欢,询问究竟能拍出多少张让久美子满意的照片。
1<=n,r,c<=3000,1<=k<=10
题解
doggu在DAY1的课件上的题,结果就只看懂了第二个方法:枚举上下边界,先固定左边界p,移动右边界q直到恰满足条件,这样就有c-q+1种方案,然后移动左边界....其实就是双指针,双指针复杂度为O(n),于是这种方法就是O(n3),得到了30opts的高分(?????)
在一波询问之后终于对正解有了一点理解,代码里面(照着std)写的是枚举左边界,那就用左边界讲解。
我们在固定一个左边界之后,右边界初始在最右边,没有上下边界,将这区域的点排序(以行为第一关键字,列为第二关键字),然后就可以形成一个链表,考虑如果点x是从上边界下来遇到的第一个节点,他的前驱pre和往后跳(k-1)的后继 next,这个点的val就是(row[x]-row[pre])*(r-row[next]+1),就是上下边界的活动范围的乘积。
记录下所有在区域内的点的val的和ret,现在考虑将右边界缩小,假设x点变成区域外,那么ret-=val[x],更新链表,并且next[x]和x往上的k-1个点的val都需要更新,还有ret也要更新。
如何知道移动这个右边界会影响哪些点,对于每列开个vector就好了。
排序那一块,看着std的打的,dx,dy记录点的坐标,id就是每个点的编号,mp是原来的人的筛选后的编号。这种cmp的写法只改变id数组,使得(dx[id[i]],dy[id[i]])变得有序。
要多存入一些边界点,防止出界。
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=3025; int n,r,c,k; int x[maxn],y[maxn]; int dx[maxn],dy[maxn],id[maxn],mp[maxn]; int pre[maxn],nxt[maxn],row[maxn]; vector<int> col[maxn]; ll ret,ans,val[maxn]; template<class T>inline void read(T &x) x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48);ch=getchar(); bool cmp(int a,int b) if(dx[a]==dx[b]) return dy[a]<dy[b]; return dx[a]<dx[b]; void del(int t) nxt[pre[t]]=nxt[t]; pre[nxt[t]]=pre[t]; ret-=val[t]; int x=nxt[t],y=nxt[t]; for(int i=1;i<k;i++) y=nxt[y]; for (int i=1;i<=k;i++) ll v=(dx[x]-dx[pre[x]])*(r-dx[y]+1); ret+=v-val[x]; val[x]=v; x=pre[x]; y=pre[y]; ll get(int l) int cnt=0;ll sum=0; ret=0; for(int i=1;i<=n;i++) if(y[i]>=l) dx[++cnt]=x[i]; dy[cnt]=y[i]; mp[i]=cnt; for(int i=1;i<=10;i++) dx[++cnt]=0,dx[++cnt]=r+1; for(int i=1;i<=cnt;i++) id[i]=i; sort(id+1,id+cnt+1,cmp); for(int i=1;i<cnt;i++) pre[id[i+1]]=id[i]; nxt[id[i]]=id[i+1]; pre[id[1]]=id[1]; nxt[id[cnt]]=id[cnt]; for(int i=1;i<=cnt;i++) int now=i; for(int j=1;j<k;j++) now=nxt[now]; val[i]=(dx[i]-dx[pre[i]])*(r-dx[now]+1); ret+=val[i]; for(int i=c;i>=l;i--) sum+=ret; for(unsigned int j=0;j<col[i].size();j++) del(mp[col[i][j]]); return sum; int main() freopen("baritone.in","r",stdin); freopen("baritone.out","w",stdout); read(r);read(c);read(n);read(k); for(int i=1;i<=n;i++) read(x[i]);read(y[i]); col[y[i]].push_back(i); for(int i=1;i<=c;i++) ans+=get(i); printf("%lld",ans);
以上是关于暑假D21的主要内容,如果未能解决你的问题,请参考以下文章