暑假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;
            
    
jigsaw

 

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);
View Code

 

以上是关于暑假D21的主要内容,如果未能解决你的问题,请参考以下文章

世界上最遥远的距离莫过于:「暑假前」和「暑假后」

2021软件创新实验室暑假集训总结篇

2021软件创新实验室暑假集训总结篇

2021软件创新实验室暑假集训SpringBoot框架

2021软件创新实验室暑假集训SpringBoot框架

回想暑假,用时间铸就规律生活