【比赛】高一下期末
T1 月下“毛景树” 100Pts
题面
树链剖分板子,边权转点权。
由于这题要支持区间修改和区间覆盖两个操作,所以要开两个 lazy
标记,注意覆盖的优先级要比增加高,所以更新覆盖的标记时要把增加的清零,pushdown
时要按照先覆盖后增加的顺序进行。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
struct edge{
int next,to,w;
}e[N*2];
int h[N],cnt;
void add(int u,int v,int w){
e[++cnt]={h[u],v,w};
h[u]=cnt;
}
int a[N],w[N],dep[N],fa[N],siz[N],son[N],top[N],id[N],tot;
void dfs1(int x,int f){
dep[x]=dep[f]+1;
fa[x]=f;
siz[x]=1;
int maxc=0;
for(int i=h[x];i;i=e[i].next){
int to=e[i].to;
if(to==f)continue;
a[to]=e[i].w;
dfs1(to,x);
siz[x]+=siz[to];
if(siz[to]>maxc){
maxc=siz[to];
son[x]=to;
}
}
}
void dfs2(int x,int topf){
top[x]=topf;
id[x]=++tot;
w[tot]=a[x];
if(!son[x])return;
dfs2(son[x],topf);
for(int i=h[x];i;i=e[i].next){
int to=e[i].to;
if(to==fa[x]||to==son[x])continue;
dfs2(to,to);
}
}
struct tree{
int l,r,dat,lz1,lz2;//lz1¸²¸Ç lz2Ôö¼Ó
}t[N<<2];
void pushup(int k){
t[k].dat=max(t[k<<1].dat,t[k<<1|1].dat);
}
void pushdown(int k){
if(t[k].lz1!=-1){
// t[k].lz2=0;
t[k<<1].lz2=t[k<<1|1].lz2=0;
t[k<<1].lz1=t[k<<1|1].lz1=t[k].lz1;
t[k<<1].dat=t[k<<1|1].dat=t[k].lz1;
t[k].lz1=-1;
}
if(t[k].lz2){
t[k<<1].lz2+=t[k].lz2;
t[k<<1].dat+=t[k].lz2;
t[k<<1|1].lz2+=t[k].lz2;
t[k<<1|1].dat+=t[k].lz2;
t[k].lz2=0;
}
}
void build(int k,int l,int r){
t[k]={l,r,w[l],-1,0};
if(l==r)return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void update1(int k,int l,int r,int val){
if(l<=t[k].l&&t[k].r<=r){
t[k].lz1=t[k].dat=val;
t[k].lz2=0;
return;
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(l<=mid)update1(k<<1,l,r,val);
if(r>mid)update1(k<<1|1,l,r,val);
pushup(k);
}
void update2(int k,int l,int r,int val){
if(l<=t[k].l&&t[k].r<=r){
t[k].lz2+=val;
t[k].dat+=val;
return;
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(l<=mid)update2(k<<1,l,r,val);
if(r>mid)update2(k<<1|1,l,r,val);
pushup(k);
}
int query(int k,int l,int r){
if(l<=t[k].l&&t[k].r<=r)return t[k].dat;
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
int ans=0;
if(l<=mid)ans=max(ans,query(k<<1,l,r));
if(r>mid)ans=max(ans,query(k<<1|1,l,r));
return ans;
}
void uprange1(int x,int y,int val){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
update1(1,id[top[x]],id[x],val);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update1(1,id[x]+1,id[y],val);
}
void uprange2(int x,int y,int val){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
update2(1,id[top[x]],id[x],val);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update2(1,id[x]+1,id[y],val);
}
int qrange(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans=max(ans,query(1,id[top[x]],id[x]));
x=fa[top[x]];
}
if(x==y)return ans;
if(dep[x]>dep[y])swap(x,y);
ans=max(ans,query(1,id[x]+1,id[y]));
return ans;
}
int n,x,y,z;
string op;
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs1(1,1);
dfs2(1,1);
build(1,1,n);
while(cin>>op){
if(op=="Stop")break;
else if(op=="Change"){
scanf("%d%d",&x,&y);
int to1=e[x*2-1].to,to2=e[x*2].to,to;
if(id[to1]>id[to2])to=to1;
else to=to2;
update1(1,id[to],id[to],y);
}
else if(op=="Cover"){
scanf("%d%d%d",&x,&y,&z);
if(x==y)continue;
uprange1(x,y,z);
}
else if(op=="Add"){
scanf("%d%d%d",&x,&y,&z);
if(x==y)continue;
uprange2(x,y,z);
}
else{
scanf("%d%d",&x,&y);
int ans=qrange(x,y);
printf("%d\n",ans);
}
}
return 0;
}
T2 上课安排 100Pts
题面
很抽象的一道题,应该是个构造...吧?
赛后题解:
首先对于30%甚至50%的数据,打表是完全没问题的,那么对于可打表的数据,我们可以发现一个规律:最多可以取的次数为n-2,并且恒成立,下面给出证明:
(1):n为奇数
n最小为5,此时最大次数为3,可为{1},{2,3},{2,4,5},对于7,我们可以在每一组后添1个7,因为原组合不互相包含,那么加上一个7后仍不互相包含,然后将6作为单独一组,以此类推,所有为奇数的n均可以用这种方式构造出。
(2):n为偶数
同(1),n最小为4,最大次数为2,{1},{2,3},对于6可以在每一组后添1个6,因为原组合不互相包含,那么加上一个6后仍不互相包含,然后将5作为单独一组,以此类推,所有为偶数的n均可以用这种方式构造出。证毕。
我的解题历程:
- 观察大样例并仔细手玩
- 发现了一个很抽象的规律
规律:从多往少排,对于前一半每次将从后往前第
个数加 ,并将第 个数删去;对于后一半将前两个数加起来除以 ,如果其和 的奇偶性不同就加 。
- 过了
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> a;
int main(){
freopen("course.in","r",stdin);
freopen("course.out","w",stdout);
int n;
cin>>n;
cout<<n-2<<"\n";
for(int i=1;i<=n-2;i++)a.push_back(i);
if(n&1){
int aa=(n-2)/2+1;
int tt=1;
for(int i=n-2;i>=aa;i--){
cout<<i<<" ";
for(auto &j:a)cout<<j<<" ";
if(i==aa)break;
int ed=a.size()-tt;
a[ed]+=2;
a.erase(a.end()-tt-1);
tt++;
cout<<"\n";
}
cout<<"\n";
for(int i=aa-1;i>=1;i--){
cout<<i<<" ";
a[0]=(a[0]+a[1])/2;
if((a[0]&1)==1)a[0]++;
a.erase(a.begin()+1);
for(auto &j:a)cout<<j<<" ";
cout<<"\n";
}
}
else{
int aa=(n-2)/2;
int tt=1;
for(int i=n-2;i>=aa;i--){
cout<<i<<" ";
for(auto &j:a)cout<<j<<" ";
if(i==aa)break;
int ed=a.size()-tt;
a[ed]+=2;
a.erase(a.end()-tt-1);
tt++;
cout<<"\n";
}
cout<<"\n";
for(int i=aa-1;i>=1;i--){
cout<<i<<" ";
a[0]=(a[0]+a[1])/2;
if((a[0]&1)==0)a[0]++;
a.erase(a.begin()+1);
for(auto &j:a)cout<<j<<" ";
cout<<"\n";
}
}
return 0;
}
T3 偷天换日 10Pts
题面
树上 DP,但过于抽象的数据,过于吓人的图和 T1 给的时间压力让我直接跳了这题,只拿了特判大样例的 10Pts。
由于输入是按深搜来的,所以我们要一边 dfs 一边输入一边 DP。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=605;
ll n,m,ans,a,b;
int cnt=1;
int lc[N],rc[N];
ll w[N],c[N],f[N][N];
void dfs(int x){
cin>>w[cnt]>>c[cnt];
if(c[cnt]==0){
lc[x]=++cnt;
dfs(cnt);
int now=w[lc[x]]*2;
for(int i=now;i<=n;i++)f[x][i]=f[lc[x]][i-now];
rc[x]=++cnt;
dfs(cnt);
now=w[rc[x]]*2;
for(int i=n;;i--){
if(i<now)break;
for(int j=0;j<=i-now;j++){
f[x][i]=max(f[x][i],f[x][i-j-now]+f[rc[x]][j]);
}
}
}
else{
for(int i=1;i<=c[cnt];i++){
cin>>a>>b;
for(int j=n;j>=b;j--)f[x][j]=max(f[x][j],f[x][j-b]+a);
}
}
}
int main(){
freopen("steal.in","r",stdin);
freopen("steal.out","w",stdout);
cin>>n;
n--;
dfs(1);
for(int i=0;i<=n-w[1]*2;i++)ans=max(ans,f[1][i]);
cout<<ans;
return 0;
}
T4 Hotel 旅馆 83Pts
题面
又是一道小山海经。线段树维护左右延伸距离和总距离,查找时先搜左边再中间最后右边,第一个满足的位置即为所求;清空时就是线段树的覆盖。
不过这题数据水了,暴力能过,洛谷也能过。
update:加了组数据,卡了暴力。
点击查看代码
#include<iostream>
#include<iomanip>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=50005;
int n,m,l,r,ask,k;
struct tree{
int l,r,sum,lsum,rsum,lazy;
}t[N<<2];
void pushup(int k){
int len=t[k].r-t[k].l+1;
t[k].lsum=t[k<<1].lsum;
t[k].rsum=t[k<<1|1].rsum;
if(t[k].lsum==len-(len>>1))t[k].lsum+=t[k<<1|1].lsum;
if(t[k].rsum==len>>1)t[k].rsum+=t[k<<1].rsum;
t[k].sum=max(max(t[k<<1].sum,t[k<<1|1].sum),t[k<<1].rsum+t[k<<1|1].lsum);
}
void build(int k,int l,int r){
t[k].l=l;
t[k].r=r;
t[k].lsum=t[k].rsum=t[k].sum=r-l+1;
t[k].lazy=-1;
if(l==r)return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void pushdown(int k){
if(t[k].lazy!=-1){
int len=t[k].r-t[k].l+1;
t[k<<1].lazy=t[k<<1|1].lazy=t[k].lazy;
if(t[k].lazy==0)t[k<<1].lsum=t[k<<1].rsum=t[k<<1].sum=(len-(len>>1));
else t[k<<1].lsum=t[k<<1].rsum=t[k<<1].sum=0;
if(t[k].lazy==0)t[k<<1|1].lsum=t[k<<1|1].rsum=t[k<<1|1].sum=len>>1;
else t[k<<1|1].lsum=t[k<<1|1].rsum=t[k<<1|1].sum=0;
t[k].lazy=-1;
}
}
int query(int k,int w){
if(t[k].l==t[k].r)return 1;
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(t[k<<1].sum>=w)return query(k<<1,w);
else if(t[k<<1].rsum+t[k<<1|1].lsum>=w)return mid-t[k<<1].rsum+1;
else return query(k<<1|1,w);
}
void update(int k,int l,int r,int val){
if(l<=t[k].l&&t[k].r<=r){
if(val==0)t[k].lsum=t[k].rsum=t[k].sum=t[k].r-t[k].l+1;
else t[k].lsum=t[k].rsum=t[k].sum=0;
t[k].lazy=val;
return;
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(l<=mid)update(k<<1,l,r,val);
if(r>mid)update(k<<1|1,l,r,val);
pushup(k);
}
int main(){
freopen("hotel.in","r",stdin);
freopen("hotel.out","w",stdout);
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--){
scanf("%d",&ask);
if(ask==1){
scanf("%d",&k);
if(k>t[1].sum){
printf("0\n");
continue;
}
int pos=query(1,k);
printf("%d\n",pos);
update(1,pos,pos+k-1,1);
}
else{
scanf("%d%d",&l,&r);
update(1,l,l+r-1,0);
}
}
return 0;
}
暴力
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+5;
int a[N],n,m,op,x,y;
int main(){
freopen("hotel.in","r",stdin);
freopen("hotel.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d",&op);
if(op==1){
scanf("%d",&x);
int cnt=0;
for(int i=1;i<=n;i++){
if(a[i]==0)cnt++;
else cnt=0;
if(cnt==x){
printf("%d\n",i+1-x);
for(int j=i+1-x;j<=i;j++)a[j]=1;
break;
}
}
if(cnt!=x)puts("0");
}
else{
scanf("%d%d",&x,&y);
for(int i=x;i<=x+y-1;i++)a[i]=0;
}
}
return 0;
}
T5 基因匹配Match 60Pts
题面
显然 60 分的部分分就是一个裸的 LCS,直接暴力即可。
然后仔细观察,我们发现一个数最多只会出现
60分暴力
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N],b[N];
int f[2][N];
int main(){
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
scanf("%d",&n);
n*=5;
for(register int i=1;i<=n;i++)scanf("%d",&a[i]);
for(register int i=1;i<=n;i++)scanf("%d",&b[i]);
for(register int i=1;i<=n;i++){
for(register int j=1;j<=n;j++){
f[i&1][j]=max({f[i&1][j],f[i-1&1][j],f[i&1][j-1]});
if(a[i]==b[j])f[i&1][j]=max(f[i&1][j],f[i-1&1][j-1]+1);
}
}
cout<<f[n&1][n];
return 0;
}
点击查看代码
#include<bits/stdc++.h>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,a[N],b,c[N];
int f[N];
void add(int x,int val){
while(x<=n){
c[x]=max(c[x],val);
x+=lowbit(x);
}
}
int query(int x){
int ans=0;
while(x){
ans=max(ans,c[x]);
x-=lowbit(x);
}
return ans;
}
vector<int> pos[N/5];
int main(){
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
cin>>n;
n*=5;
for(int i=1;i<=n;i++){
cin>>a[i];
pos[a[i]].push_back(i);
}
for(int i=1;i<=n;i++){
cin>>b;
for(int j=pos[b].size()-1;j>=0;j--){
int p=pos[b][j];
f[p]=query(p-1)+1;
add(p,f[p]);
}
}
int ans=0;
for(int i=1;i<=n;i++)ans=max(ans,f[i]);
cout<<ans;
return 0;
}
后记
-
怎么这次又不发冰棍了?
-
考试前一天晚上
设置比赛时忘关投屏了,导致所有人都知道 T1 是什么了,所以怎么只有 个人过捏 -
对我们特判大样例一事进行了批评:
“你们这
分的都是特判(大样例)的吧”
“下次在这么干可就不给大样例了”
- T4 数据过水招致了不少人的怨恨。
“加上这题暴力我能比他俩都高!” —— oistars
-
虽然 @Elaina-0 没有来集训,但他参加了考试,并取得了 rk2 的好成绩。
-
23426 的 OIer 正好能凑出一个宿舍!
update:yht 退役了,这下真的一个宿舍都凑不齐了
-
凭什么我们吃饭不能免费?
-
集训第一天就考试;总结:史。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探