Loading

落实2

落实2

考试降智了

T1 疯狂的馒头

显而易见,对于这个馒头的有效操作只是最后一次操作,所以我们倒序枚举,用并查集维护这个馒头有没有涂色就行了。

#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=1e7+5;
int ff[N];
int find(int x){
	if(ff[x]==x) return x;
	return ff[x]=find(ff[x]);
}
int n,m,p,q;
int color[N];
int main(){
	n=read(),m=read(),p=read(),q=read();
	for(int i=1;i<=n+1;i++) ff[i]=i;
	for(int i=m;i;i--){
		int x=(i*p+q)%n+1,y=(i*q+p)%n+1;
		if(x>y) swap(x,y);
		for(int j=find(x);j<=y;j=find(j)){
			ff[j]=j+1;
			color[j]=i;
		}
	}
	for(int i=1;i<=n;i++) printf("%d\n",color[i]);
	return 0;
}

总结:最后开的,有点神志不清,想着分块去了。

T2 奶酪

就是并查集判一下的无脑题,其实应该作为T1。

#include<bits/stdc++.h>
using namespace std;
#define int long long
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=1e3+5,INF=0x3f3f3f3f;
int T,n,h,r;
int ff[N];
int find(int x){
  if(ff[x]==x) return x;
  return ff[x]=find(ff[x]);
}
struct lzz{
  int x,y,z;
}node[N];
int dis(int i,int j){
  int xx=(node[i].x-node[j].x)*(node[i].x-node[j].x);
  int yy=(node[i].y-node[j].y)*(node[i].y-node[j].y);
  int zz=(node[i].z-node[j].z)*(node[i].z-node[j].z);
  return xx+yy+zz;
}
signed main(){
  T=read();
  while(T--){
    n=read(),h=read(),r=read();
    for(int i=1;i<=n+2;i++) ff[i]=i;
    for(int i=1;i<=n;i++){
      node[i].x=read(),node[i].y=read(),node[i].z=read();
      if(node[i].z<=r) ff[find(i)]=n+1;
      if(node[i].z+r>=h) ff[find(i)]=n+2;
    }
    for(int i=1;i<n;i++)
      for(int j=i+1;j<=n;j++)
	if(dis(i,j)<=4*r*r){
	  int x=find(i),y=find(j);
	  if(x!=y) ff[x]=y;
	}
    if(find(n+1)==find(n+2)) printf("Yes\n");
    else printf("No\n");
  }
  return 0;
}

总结:没开 long long

T3 树

就是要用树剖碾倍增标算才爽。

我们先正常把树剖成链,考虑用线段树维护树上的信息。设二元组 \((mx,tot)\) 表示这个区间里的最大值和有多少个递减的数,我们用一个结构体存起来。再定义一个函数 \(query\) 专门求区间 \([l,r]\) 中大于 \(v\) 的数的个数。

这个函数可以这样算:

int query(int x,int l,int r,int v){
  if(l==r) return sum[x].mx>v;
  int mid=(l+r)>>1;
  if(sum[rs].mx<=v) return query(ls,l,mid,v);
  else return sum[x].tot-sum[rs].tot+query(rs,mid+1,r,v);
}

对于建树,我们可以这样建:注意维护 sum[x] 的式子,自己画画图就懂了。

void build(int x,int l,int r){
  if(l==r){sum[x]=(zfz){w[seq[l]],1};return;}
  int mid=(l+r)>>1;
  build(ls,l,mid);
  build(rs,mid+1,r);
  sum[x]=(zfz){max(sum[ls].mx,sum[rs].mx),sum[rs].tot+query(ls,l,mid,sum[rs].mx)};
}

询问就正常的区间询问,不过可能要分的细一点:

zfz operator +(const zfz&x,const zfz&y){return (zfz){max(x.mx,y.mx),x.tot+y.tot};}
zfz Query(int x,int l,int r,int xl,int xr,int v){
  if(xl<=l&&xr>=r) return (zfz){sum[x].mx,query(x,l,r,v)};
  int mid=(l+r)>>1;
  if(xl<=mid){
    if(mid<xr){
      zfz tmp=Query(rs,mid+1,r,xl,xr,v);
      return Query(ls,l,mid,xl,xr,max(v,tmp.mx))+tmp;
    }
    else return Query(ls,l,mid,xl,xr,v);
  }
  else return Query(rs,mid+1,r,xl,xr,v);
}

这个分类讨论也可以自己画图。

最后由于 \(x,y\) 一定是祖孙关系,所以只用跳 \(x\) 就好了

while(Q--){
    int x=read(),y=read(),z=read();
    int ans=0;
    while(top[x]!=top[y]){
      zfz tmp=Query(1,1,n,dfn[top[x]],dfn[x],z);
      ans+=tmp.tot;
      z=max(tmp.mx,z);
      x=fa[top[x]];
    }
    zfz tmp=Query(1,1,n,dfn[y],dfn[x],z);
    ans+=tmp.tot;
    printf("%d\n",ans);
  }

代码:

#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=1e5+5,INF=2e9;
int n,Q,w[N],ww[N];
int hd[N],nx[N<<1],to[N<<1],tot;
void adde(int u,int v){
  nx[++tot]=hd[u];to[tot]=v;hd[u]=tot;
  nx[++tot]=hd[v];to[tot]=u;hd[v]=tot;
}
int fa[N],dep[N],son[N],sz[N];
void dfs1(int u,int father){
  dep[u]=dep[father]+1;fa[u]=father;sz[u]=1;
  for(int i=hd[u];i;i=nx[i]){
    int v=to[i];
    if(v==father) continue;
    dfs1(v,u);
    sz[u]+=sz[v];
    if(sz[son[u]]<=sz[v]) son[u]=v;
  }
}
int top[N],dfn[N],seq[N],dfstime;
void dfs2(int u,int anc){
  top[u]=anc;dfn[u]=++dfstime;seq[dfstime]=u;
  if(son[u]) dfs2(son[u],anc);
  for(int i=hd[u];i;i=nx[i]){
    int v=to[i];
    if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
  }
}
#define ls x<<1
#define rs x<<1|1
struct zfz{
  int mx,tot;
}sum[N<<2];
zfz operator +(const zfz&x,const zfz&y){return (zfz){max(x.mx,y.mx),x.tot+y.tot};}
int query(int x,int l,int r,int v){
  if(l==r) return sum[x].mx>v;
  int mid=(l+r)>>1;
  if(sum[rs].mx<=v) return query(ls,l,mid,v);
  else return sum[x].tot-sum[rs].tot+query(rs,mid+1,r,v);
}
void build(int x,int l,int r){
  if(l==r){sum[x]=(zfz){w[seq[l]],1};return;}
  int mid=(l+r)>>1;
  build(ls,l,mid);
  build(rs,mid+1,r);
  sum[x]=(zfz){max(sum[ls].mx,sum[rs].mx),sum[rs].tot+query(ls,l,mid,sum[rs].mx)};
}
zfz Query(int x,int l,int r,int xl,int xr,int v){
  if(xl<=l&&xr>=r) return (zfz){sum[x].mx,query(x,l,r,v)};
  int mid=(l+r)>>1;
  if(xl<=mid){
    if(mid<xr){
      zfz tmp=Query(rs,mid+1,r,xl,xr,v);
      return Query(ls,l,mid,xl,xr,max(v,tmp.mx))+tmp;
    }
    else return Query(ls,l,mid,xl,xr,v);
  }
  else return Query(rs,mid+1,r,xl,xr,v);
}
int main(){
  n=read();Q=read();
  for(int i=1;i<=n;i++) w[i]=read();
  for(int i=1;i<n;i++){
    int x=read(),y=read();
    adde(x,y);
  }
  dfs1(1,0);
  dfs2(1,1);
  build(1,1,n);
  while(Q--){
    int x=read(),y=read(),z=read();
    int ans=0;
    while(top[x]!=top[y]){
      zfz tmp=Query(1,1,n,dfn[top[x]],dfn[x],z);
      ans+=tmp.tot;
      z=max(tmp.mx,z);
      x=fa[top[x]];
    }
    zfz tmp=Query(1,1,n,dfn[y],dfn[x],z);
    ans+=tmp.tot;
    printf("%d\n",ans);
  }
  return 0;
}

总结:神题没有总结。

T4 可见点阵(可见点数)

正经解法:

对于一对有贡献的点对,他们肯定是互质的,这也很好证明:

存在点对 \((x,y)\) 满足 \(gcd(x,y)=1\),则对于点对 \((kx,ky)(k \in N^+,k>1)\) 一定会被 \((x,y)\) 遮住,所以可见的点对一定是互质的。

那就好办了,我们只用线性筛出 \(\phi\) 函数(欧拉函数/\(phi\) 函数)然后累加两遍,再加上一个 \((1,1)\) 这个点就好了。

#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=1e3+5,INF=0x3f3f3f3f;
int T;
int n=1000;
int phi[N],prime[N],vis[N],cnt;
void init(){
  phi[1]=1;
  for(int i=2;i<=n;i++){
    if(!vis[i]){phi[i]=i-1;prime[++cnt]=i;}
    for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
      vis[i*prime[j]]=1;
      if(i%prime[j]==0){
	phi[i*prime[j]]=phi[i]*prime[j];
	break;
      }
      else phi[i*prime[j]]=phi[i]*phi[prime[j]];
    }
  }
}
int main(){
  T=read();
  init();
  for(int i=1;i<=T;i++){
    n=read();
    int ans=0;
    for(int j=1;j<=n;j++) ans+=phi[j];
    printf("%d %d %d\n",i,n,ans*2+1);
  }
  return 0;
}

总结:看到 \(n<=1000\) 就打表水过了。。。复杂度 \(O(T)\) 吊锤std!

posted @ 2021-02-22 09:32  Quick_Kk  阅读(74)  评论(0编辑  收藏  举报