2023.10.11

数学一检 rk1。

2020.11.28 NOIP 提高 A 组 模拟。

A

\([1,n]\) 染色,使得 \(\forall 1\le i<j\le n,i-j\in P\)\(col_i\not=col_j\).

最小化颜色种数,给出方案。

\(n\le 10^4\).


\(n\ge 7\) 时答案至少为 \(4\).

可以证明 \(n\ge 7\) 时答案一定为 \(4\).

\([1,n]\)\(1,2,3,4,1,\dots\) 的顺序染色,那么颜色相同的差值一定非质数。

这是因为 \(4\) 是第一个合数。

对于 \(n\in [1,6]\) 暴力。

#include<bits/stdc++.h>
#define N 10010
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n;
int main(){
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	n=read();
	if(n==1)printf("1\n1\n");
	if(n==2)printf("1\n1 1\n");
	if(n==3)printf("2\n1 1 2\n");
	if(n==4)printf("2\n1 1 2 2\n");
	if(n==5)printf("3\n1 1 2 2 3\n");
	if(n==6)printf("3\n1 1 2 2 3 3\n");
	if(n>=7){
		puts("4");
		for(int i=1;i<=n;i++)
			printf("%d ",(i-1)%4+1);
		printf("\n");
	}
	
	return 0;
}

B

给定一个长为 \(m\) 的序列 \(a\).

构造一个长为 \(m\) 的序列 \(b\),满足 \(b_i\in[0,n],\sum\limits_{i=1}^{m}a_ib_i\le D\).

最大化 \(\sum b_i+k\min_{i=1}^{m}\{b_i\}\).

多测。

\(T\le 5\)\(1\le n\le 10^9\)\(1\le k,m\le 2\times 10^5\)


钦定 \(\min\) 后可以用 \(O(\log m)\) 的时间求出答案式。时间复杂度 \(O(nm)\).

这个东西是个非严格单峰函数。然后写了个三分有 95pts.

因为是非严格单峰,所以可以有 \(f(lmid)=f(rmid)\),可以直接分两次跳。

比较懒,我把判断条件从 \(>\) 变成 \(\ge\) 就过了。

#include<bits/stdc++.h>
#define ll long long
#define N 200010
using namespace std;
ll read(){
	ll x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
ll n,k,D;
int m,a[N],s[N];
int binary(ll mn,ll sum){
	if(m<=2)return 0;
	int l=0,r=m-2,mid,ans=0;
	while(l<=r){
		mid=(l+r)>>1;
		if((n-mn)*s[mid]<=sum)l=mid+1,ans=mid;
		else r=mid-1;
	}
	return ans;
}
ll calc(ll mn){
	if(mn<0)return 0;
	ll now=mn*k+mn*m,sum=D-mn*s[m],tp;
	if(sum<0)return 0;
	int pos=binary(mn,sum);
	sum-=(n-mn)*s[pos],now+=(n-mn)*pos;
	tp=min(n-mn,sum/a[pos+1]),now+=tp;
	return now;
}
ll find(ll L,ll R){
	ll Lmid=L,Rmid=R;
	while(L+2<R){
		Lmid=L+(R-L)/3;
		Rmid=R-(R-L)/3;
		if(calc(Lmid)>=calc(Rmid))R=Rmid;
		else L=Lmid;
	}
	return (Lmid+Rmid)/2;
}
void solve(){
	n=read(),m=read(),k=read(),D=read();
	ll ans=0;
	for(int i=1;i<=m;i++)a[i]=read();
	sort(a+1,a+1+m);
	for(int i=1;i<=m;i++)s[i]=s[i-1]+a[i];
	ll pos=find(0,min(n,D/s[m]));
	ans=calc(pos);
	for(int i=1;i<=10;i++)
		ans=max(ans,max(calc(pos-i),calc(pos+i)));
	printf("%lld\n",ans);
}
int main(){
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	int T=read();
	while(T--)solve();
	
	return 0;
}

C

给出一棵树,问沿着 \(l\rightarrow r\) 的路径走,有多少 \(k\) 满足走了 \(k\) 步之后正好到达 \(k\).

\(n,m\le 3\times 10^5\)\(\mathrm{ML}=512\mathrm{MB}\).

考虑拆成 \(l\rightarrow lca+lca\rightarrow r-lca\) 的贡献。

对于 \(l\rightarrow lca\),有 \(\operatorname{dist}(l,k)=k\).

\(dep_l=dep_k+k\).

对于 \(lca\rightarrow r\),有 \(\operatorname{dist}(l,lca)+\operatorname{dist}(lca,k)=k\).

\(dep_k-k=2dep_{lca}-dep_l\).

设一个偏移量,树剖+主席树即可。不要开两颗主席树。

写 3k 就没了。

#include<bits/stdc++.h>
#define N 300010
#define M 900010
#define dlt 300001
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,m;
vector<int>e[N];
int fa[N],dep[N],siz[N],top[N],son[N],dfn[N],id[N],tim;
void dfs1(int u){
	siz[u]=1;
	for(int v:e[u]){
		if(dep[v])continue;
		fa[v]=u,dep[v]=dep[u]+1;
		dfs1(v),siz[u]+=siz[v];
		if(siz[son[u]]<siz[v])son[u]=v;
	}
}
void dfs2(int u,int t){
	dfn[u]=++tim,top[u]=t;
	id[tim]=u;
	if(son[u])dfs2(son[u],t);
	for(int v:e[u]){
		if(v==fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
}
int getlca(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=fa[top[u]];
	}
	return dep[u]>dep[v]?v:u;
}
struct Node{
	int ls,rs,cnt;
};
#define ls(x) tr[x].ls
#define rs(x) tr[x].rs
#define cnt(x) tr[x].cnt
struct Tree{
	int rt[M],node;
	Node tr[M<<5];
	void clear(){
		memset(rt,0,sizeof(rt)),node=0;
		for(int i=1;i<(M<<5);i++)
			ls(i)=rs(i)=cnt(i)=0;
	}
	int build(int l,int r){
		int p=++node;
		if(l==r)return p;
		int mid=(l+r)>>1;
		ls(p)=build(l,mid);
		rs(p)=build(mid+1,r);
		return p;
	}
	void init(){
		rt[0]=build(1,n*2+dlt);
	}
	int modify(int pre,int l,int r,int x){
		int p=++node;tr[p]=tr[pre];
		if(l==r){
			cnt(p)++;return p;
		}
		int mid=(l+r)>>1;
		if(x<=mid)ls(p)=modify(ls(pre),l,mid,x);
		else rs(p)=modify(rs(pre),mid+1,r,x);
		return p;
	}
	int query(int p,int q,int l,int r,int x){
		if(l==r)return cnt(q)-cnt(p);
		int mid=(l+r)>>1;
		if(x<=mid)return query(ls(p),ls(q),l,mid,x);
		return query(rs(p),rs(q),mid+1,r,x);
	}
}T;
struct Q{
	int l,r,lca,ans;
}q[N];
int query(int u,int lca,int x){
	int ans=0;
	while(top[u]!=top[lca]){
		ans+=T.query(T.rt[dfn[top[u]]-1],T.rt[dfn[u]],1,n*2+dlt,x);
		u=fa[top[u]];
	}
	ans+=T.query(T.rt[dfn[lca]-1],T.rt[dfn[u]],1,n*2+dlt,x);
	return ans;
}
int main(){
	freopen("query.in","r",stdin);
	freopen("query.out","w",stdout);
	//dlt=300001,x->900001,Tree->900001*30
	n=read(),m=read();
	for(int i=1,u,v;i<n;i++){
		u=read(),v=read();
		e[u].push_back(v),e[v].push_back(u);
	}
	dep[1]=1,dfs1(1),dfs2(1,1);
	for(int i=1,l,r;i<=m;i++){
		l=read(),r=read();
		q[i]=(Q){l,r,getlca(l,r),0};
	}
	T.init();
	for(int i=1;i<=n;i++)
		T.rt[i]=T.modify(T.rt[i-1],1,n*2+dlt,id[i]+dep[id[i]]+dlt);
	for(int i=1;i<=m;i++)
		q[i].ans=query(q[i].l,q[i].lca,dep[q[i].l]+dlt);
	T.clear();
	for(int i=1;i<=n;i++)
		T.rt[i]=T.modify(T.rt[i-1],1,n*2+dlt,dep[id[i]]-id[i]+dlt);
	for(int i=1,l,r,lca;i<=m;i++){
		q[i].ans+=query(q[i].r,q[i].lca,2*dep[q[i].lca]-dep[q[i].l]+dlt);
		if(q[i].lca+dep[q[i].lca]==dep[q[i].l])q[i].ans--;
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",q[i].ans);
	
	return 0;
}
  • 树剖干嘛?直接树上差分没了。

D

一个序列,支持

  • 单点修改

  • 查询 \([l,r]\) 中无重子区间数。

\(n,m\le 2\times 10^5\)\(a_i\le n\).

写一下 \(O(n^2)\) 大众分。

每次修改时对每个 \(i\) 维护从 \(i\) 开始的极长无重区间,可以用双指针实现。

查询 \(O(n)\) 即可。时间复杂度 \(O(n^2)\).

void init(){
	int l=1,r=0;
	while(l<=n){
		while(r<n&&!flag[a[r+1]])flag[a[++r]]=true;
		rht[l]=r,flag[a[l++]]=false;
	}
}
//main
init();
for(int i=1,opt,l,r;i<=m;i++){
	opt=read(),l=read(),r=read();
	if(opt==1)a[l]=r,init();
	if(opt==2){
		ll ans=0;
		for(int j=l;j<=r;j++)
			ans+=min(rht[j],r)-j+1;
		printf("%lld\n",ans);
	}
}

\(10+95+100+40=245\),比较抽象。

posted @ 2023-10-11 15:09  SError  阅读(33)  评论(1编辑  收藏  举报