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\),比较抽象。