Loading

落实1

落实1

这应该是我落实得最快的一次了。

T1 平板涂色

这道题我写的有点久,看到这种矩形就烦。但最后还是想到了一种神奇的做法:化矩形为图,我们能不能直接用图上的关系来表示这些矩形的关系呢?显然是可以的。怎么存呢?

for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
      if(i==j) continue;
      if(node[i].xl==node[j].xr&&!(node[i].yr<node[j].yl||node[i].yl>node[j].yr)) adde(j,i);
    }
  }

这么存。把他上面的直接相连的相接触的矩形连一条边到下面这个矩形。

注意,别把 i!=j 写在 for 循环语句上,我也不知道为什么会错。。。

存了边后,我一看数据范围!原来是暴搜可以过的范围!好家伙,直接上暴搜。

我们先从当前入度为 \(0\) 的点的颜色开始消,用拓扑每次消掉颜色相同的矩形,换一次颜色就加一次更换颜色次数,最后加上一个小小的剪枝就可以通过此题了!

我觉得我这个搜索有点丑,但是还是 \(O(n^3)\) 的算法。

#include<bits/stdc++.h>
using namespace std;
inline int read(){
  int ans=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
  while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
  return ans*f;
}
const int N=25;
int n,start,ans=0x3f3f3f3f;
struct lzz{
  int xl,yl,xr,yr;
  int color;
}node[N];
int hd[N],to[N],nx[N],in[N],tot;
int vis[N];
void adde(int u,int v){
  nx[++tot]=hd[u];to[tot]=v;in[v]++;hd[u]=tot;
}
void dfs(int num,int cnt){
  if(num>ans) return;
  if(cnt==n){
    ans=min(ans,num);
    return;
  }
  queue<int> q;
  int color,sum=0;
  int tmpvis[N],tmpin[N];
  //copy
  for(int i=1;i<=n;i++) tmpvis[i]=vis[i],tmpin[i]=in[i];
  //doit
  for(int i=1;i<=n;i++){
    if(!in[i]&&!vis[i]){
      color=node[i].color;
      for(int j=1;j<=n;j++) 
	if(!in[j]&&!vis[j]&&node[j].color==color) sum++,q.push(j);
      while(!q.empty()){
	int u=q.front();q.pop();
	if(vis[u]) continue;
	vis[u]=1;
	for(int k=hd[u];k;k=nx[k]){
	  int v=to[k];
	  in[v]--;
	  if(!vis[v]&&node[v].color==color&&!in[v]){
	    q.push(v);
	    sum++;
	  }
	}
      }
      dfs(num+1,cnt+sum);
      for(int z=1;z<=n;z++) vis[z]=tmpvis[z],in[z]=tmpin[z];
      sum=0;
    }
  }
}
int main(){
  n=read();
  for(int i=1;i<=n;i++){
    node[i].xl=read(),
      node[i].yl=read(),
      node[i].xr=read(),
      node[i].yr=read(),
      node[i].color=read();
  }
  for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
      if(i==j) continue;
      if(node[i].xl==node[j].xr&&!(node[i].yr<node[j].yl||node[i].yl>node[j].yr))
	adde(j,i);
    }
  }
  dfs(0,0);
  printf("%d",ans);
  return 0;
}

总结:这题送分的,结果建图白学,建了个假图,还过了一半的点。。。

T2 火柴排队

这题就是 \(zfz\) 大佬秀操作的一题了,思路特别巧妙,具体证明以后再补(懒

就是求一波逆序对的事情。

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
inline int read(){
  int ans=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
  while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
  return ans*f;
}
const int N=1e5+5,MOD=1e8-3;
int n,ans;
struct lzz{
  int id,high,rank;
}b[N],a[N];
//treearray
int tre[N];
void upd(int x,int v){
  while(x<=n){
    tre[x]+=v;
    x+=lowbit(x);
  }
}
int ask(int x){
  int res=0;
  while(x){
    res+=tre[x];
    x-=lowbit(x);
  }
  return res;
}
bool cmp1(lzz x,lzz y){return x.high<y.high;}
bool cmp2(lzz x,lzz y){return x.id<y.id;}
int main(){
  n=read();
  for(int i=1;i<=n;i++) a[i].high=read(),a[i].id=i;
  for(int i=1;i<=n;i++) b[i].high=read(),b[i].id=i;
  sort(b+1,b+1+n,cmp1);
  sort(a+1,a+1+n,cmp1);
  for(int i=1;i<=n;i++) a[i].rank=b[i].id;
  sort(a+1,a+1+n,cmp2);
  for(int i=1;i<=n;i++){
    upd(a[i].rank,1);
    (ans+=i-ask(a[i].rank))%=MOD;
  }
  printf("%d",ans);
  return 0;
}

总结:不会从简单的式子入手找出规律 =-=

T3 线性函数

这题就是线段树,我考场也看出来了,但是实现了一半突然感觉不能实现,就白给了。其实这就是一道码力题,只要你线段树打的优美就可以过了。

其实很好实现,对于每个函数:\(f(x)=kx+b\),我们存一个 segtreek 和一个 segtreeb,来表示。

那么修改只需要线段树的单点修改即可。

我们主要来考虑怎么合并,对于线段树上的父节点和他的两个子节点,我们只需要先处理左儿子,然后再用所得的值来处理右儿子,即可满足题目的从右到左嵌套的要求。说白了,就是先序遍历,就不需要做什么改动,代码简洁明了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ls x<<1
#define rs x<<1|1
inline int read(){
  int ans=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
  while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
  return ans*f;
}
const int N=2e5+5,MOD=1e9+7;
int n,Q;
int k[N],b[N];
int segtreek[N<<2+5],segtreeb[N<<2+5];
struct node{
  int k,b;
};
void build(int x,int l,int r){
  if(l==r){
    segtreek[x]=k[l];
    segtreeb[x]=b[l];
    return;
  }
  int mid=l+r>>1;
  build(ls,l,mid);
  build(rs,mid+1,r);
  segtreek[x]=1ll*segtreek[ls]*segtreek[rs]%MOD;
  segtreeb[x]=(1ll*segtreek[rs]*segtreeb[ls]%MOD+segtreeb[rs])%MOD;
}
void update(int pos,int x,int l,int r,int vk,int vb){
  if(l==r){
    segtreek[x]=vk;
    segtreeb[x]=vb;
    return;
  }
  int mid=l+r>>1;
  if(pos<=mid) update(pos,ls,l,mid,vk,vb);
  else update(pos,rs,mid+1,r,vk,vb);
  segtreek[x]=1ll*segtreek[ls]*segtreek[rs]%MOD;
  segtreeb[x]=(1ll*segtreek[rs]*segtreeb[ls]%MOD+segtreeb[rs])%MOD;
}
node query(int x,int l,int r,int xl,int xr){
  if(xl<=l&&xr>=r) return (node){segtreek[x],segtreeb[x]};
  int mid=l+r>>1;
  node res={1,0};
  if(xl<=mid) res=query(ls,l,mid,xl,xr);
  if(xr>mid){
    node tmp=query(rs,mid+1,r,xl,xr);
    int kk=tmp.k*res.k%MOD,bb=(tmp.k*res.b%MOD+tmp.b)%MOD;
    res=(node){kk,bb};
  }
  return res;
}
signed main(){
  n=read(),Q=read();
  for(int i=1;i<=n;i++) k[i]=read(),b[i]=read();
  build(1,1,n);
  char opt;
  int x,y,z;
  while(Q--){
    opt=getchar();
    x=read(),y=read(),z=read();
    if(opt=='M') update(x,1,1,n,y,z);
    else{
      node tmp=query(1,1,n,x,y);
      printf("%d\n",(1ll*tmp.k*z%MOD+tmp.b)%MOD);
    }
  }
  return 0;
}

T4 超级钢琴

RMQ神题,思路牛的爆炸。

要求一段区间的和,首先想到前缀和,要求区间 \([i,j] (j \in [i+L-1,i+R-1])\) 的和最大的区间,很显然,假设前缀和为 sum 数组,最大值就是 \(sum[j]-sum[i-1]\),因为对于每个以 \(i\) 开头的区间,\(sum[i-1]\) 都是确定的,所以我们只要找出最大的 \(sum[j]\) 就好了。怎么找呢?区间最大值,想到好写的st表。找出最大值后,我们把他们放进大根堆里面,我们的目的就变为了从堆顶取出 \(k\) 个元素。对于任意一个堆顶的元素,取出他后,以他的 \(i\) 为开头的区间还可能有最大值产生,所以我们要在 \(j\) 的两边再用st表找两个点放进堆里面。

#include<bits/stdc++.h>
using namespace std;
inline int read(){
  int ans=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
  while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
  return ans*f;
}
const int N=5e5+5;
int n,T,L,R,logn;
long long ans;
int s[N],mx[N][25],Log[N],pos[N][25];
struct zfzgod{
  int i,l,r,j,sum;
  bool operator <(zfzgod i)const{
    return sum<i.sum;
  }
};
priority_queue<zfzgod> q;
int main(){
  n=read(),T=read(),L=read(),R=read();logn=log2(n)+1;Log[0]=-1;
  for(int i=1,x;i<=n;i++){
    x=read();
    mx[i][0]=s[i]=s[i-1]+x;
    pos[i][0]=i;
    Log[i]=Log[i>>1]+1;
  }
  for(int j=1;j<=logn;j++){
    for(int i=1;i+(1<<j)-1<=n;i++){
      if(mx[i][j-1]>mx[i+(1<<j-1)][j-1]){
	mx[i][j]=mx[i][j-1];
	pos[i][j]=pos[i][j-1];
      }
      else{
	mx[i][j]=mx[i+(1<<j-1)][j-1];
	pos[i][j]=pos[i+(1<<j-1)][j-1];
      }
    }
  }
  for(int i=1;i<=n-L+1;i++){
    int k=Log[min(i+R-1,n)-(i+L-1)+1];
    int x,y;
    if(mx[i+L-1][k]>mx[min(i+R-1,n)-(1<<k)+1][k]){
      x=mx[i+L-1][k];
      y=pos[i+L-1][k];
    }
    else{
      x=mx[min(i+R-1,n)-(1<<k)+1][k];
      y=pos[min(i+R-1,n)-(1<<k)+1][k];
    }
    q.push((zfzgod){i,i+L-1,min(i+R-1,n),y,x-s[i-1]});
  }
  while(T--){
    zfzgod tmp=q.top();q.pop();
    int x,y,k=Log[tmp.j-tmp.l];
    ans+=tmp.sum;
    if(tmp.j>tmp.l){
      if(mx[tmp.l][k]>mx[tmp.j-(1<<k)][k]){
	x=mx[tmp.l][k];
	y=pos[tmp.l][k];
      }
      else{
	x=mx[tmp.j-(1<<k)][k];
	y=pos[tmp.j-(1<<k)][k];
      }
      q.push((zfzgod){tmp.i,tmp.l,tmp.j-1,y,x-s[tmp.i-1]});
    }
    if(tmp.j<tmp.r){
      k=Log[tmp.r-tmp.j];
      if(mx[tmp.j+1][k]>mx[tmp.r-(1<<k)+1][k]){
	x=mx[tmp.j+1][k];
	y=pos[tmp.j+1][k];
      }
      else{
	x=mx[tmp.r-(1<<k)+1][k];
	y=pos[tmp.r-(1<<k)+1][k];
      }
      q.push((zfzgod){tmp.i,tmp.j+1,tmp.r,y,x-s[tmp.i-1]});
    }
  }
  printf("%lld",ans);
  return 0;
}

神题没有总结。

posted @ 2021-02-13 14:28  Quick_Kk  阅读(99)  评论(1编辑  收藏  举报