NOIP提高组模拟赛13
A. 送花
考虑选择一段以为结尾的区间,记录为上一个与颜色相同的花的位置,发现只有在这个区间才会有的贡献,而之前有贡献的,没有了的贡献,用线段树维护区间最值,支持区间修改即可
code
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1000005;
long long max(long long x,long long y){return x>y?x:y;}
int n,m,c[maxn],d[maxn],pos[maxn],las[maxn];
struct tree{
struct node{
long long max,tag;
};
node t[maxn<<2|1];
void push_down(int x){
t[x<<1].tag+=t[x].tag;
t[x<<1|1].tag+=t[x].tag;
t[x<<1].max+=t[x].tag;
t[x<<1|1].max+=t[x].tag;
t[x].tag=0;
}
void push_up(int x){
t[x].max=max(t[x<<1].max,t[x<<1|1].max);
}
void modify(int x,int l,int r,int L,int R,long long dt){
if(L<=l&&r<=R){
t[x].max+=dt;
t[x].tag+=dt;
return;
}
if(t[x].tag)push_down(x);
int mid=(l+r)>>1;
if(L<=mid)modify(x<<1,l,mid,L,R,dt);
if(R>mid)modify(x<<1|1,mid+1,r,L,R,dt);
push_up(x);
}
long long query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R)return t[x].max;
if(t[x].tag)push_down(x);
int mid=(l+r)>>1;
long long ans=0;
if(L<=mid)ans=query(x<<1,l,mid,L,R);
if(R>mid)ans=max(ans,query(x<<1|1,mid+1,r,L,R));
return ans;
}
}T;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%d",&c[i]);
for(int i=1;i<=m;++i)scanf("%d",&d[i]);
for(int i=1;i<=n;++i){pos[i]=las[c[i]];las[c[i]]=i;}
long long ans=0;
for(int i=1;i<=n;++i){
T.modify(1,1,n,pos[i]+1,i,d[c[i]]);
if(pos[i])T.modify(1,1,n,pos[pos[i]]+1,pos[i],-d[c[i]]);
ans=max(ans,T.query(1,1,n,1,n));
}
printf("%lld\n",ans);
return 0;
}
B. 星空
发现在或这条直线上的点直接距离为0,两点直接距离可以是一个点向另一个点的那两条直线作垂线的垂线段长度,考场想到这里就想不下去了,最后还是打的暴力最短路。。
旋转坐标轴,为和,计算新的坐标
两点的直接距离就变成
距离为0的点可以看做一个集合,两个集合点对数就是两个集合相乘,两个集合中任意两点最短距离就是集合中直接距离最小的两个点的距离
并查集维护集合,距离不为零尝试更新最短距离
算出最短距离后枚举为最短距离的两点,找到他们所在的集合,记录
对所有记录的两个集合去重,统计答案即可
code
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100005;
int abs(int x){return x<0?-x:x;}
int n;
struct node{
int x,y;
}d[maxn];
struct st{
int f[maxn],size[maxn];
int fa(int x){if(f[x]!=x)return f[x]=fa(f[x]);return x;}
void hb(int x,int y){x=fa(x);y=fa(y);if(x!=y){f[x]=y;size[y]+=size[x];size[x]=0;}}
int gs(int x,int y){x=size[fa(x)];y=size[fa(y)];return x*y;}
}s;
int p1[maxn],p2[maxn];
bool cmpx(int x,int y){return d[x].x<d[y].x;}
bool cmpy(int x,int y){return d[x].y<d[y].y;}
pair<int,int> shuzu[maxn << 2];
int cnt;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)s.size[i]=1;
for(int i=1;i<=n;++i)s.f[i]=i;
for(int i=1;i<=n;++i)scanf("%d%d",&d[i].x,&d[i].y);
for(int i=1;i<=n;++i){
node ls;
ls.x=d[i].x+d[i].y;
ls.y=d[i].x-d[i].y;
d[i]=ls;
}
for(int i=1;i<=n;++i)p1[i]=i;
for(int i=1;i<=n;++i)p2[i]=i;
sort(p1+1,p1+n+1,cmpx);
sort(p2+1,p2+n+1,cmpy);
for(int i=2;i<=n;++i)
if(d[p1[i]].x==d[p1[i-1]].x)
s.hb(p1[i],p1[i-1]);
for(int i=2;i<=n;++i)
if(d[p2[i]].y==d[p2[i-1]].y)
s.hb(p2[i],p2[i-1]);
int ans1=-1;
for(int i=2;i<=n;++i){
if(d[p1[i]].x!=d[p1[i-1]].x)
if(d[p1[i]].x-d[p1[i-1]].x<ans1||ans1==-1)
ans1=d[p1[i]].x-d[p1[i-1]].x;
if(d[p2[i]].y!=d[p2[i-1]].y)
if(d[p2[i]].y-d[p2[i-1]].y<ans1||ans1==-1)
ans1=d[p2[i]].y-d[p2[i-1]].y;
}
printf("%d\n",ans1);
if(ans1==-1)return 0;
for(int i=2;i<=n;++i){
if(d[p1[i]].x-d[p1[i-1]].x!=ans1)continue;
int lp=s.fa(p1[i]),rp=s.fa(p1[i-1]);
if(lp>rp)swap(lp,rp);
shuzu[++cnt] = make_pair(lp, rp);
}
for(int i=2;i<=n;++i){
if(d[p2[i]].y-d[p2[i-1]].y!=ans1)continue;
int lp=s.fa(p2[i]),rp=s.fa(p2[i-1]);
if(lp>rp)swap(lp,rp);
shuzu[++cnt] = make_pair(lp, rp);
}
int ans2=0;
sort(shuzu + 1, shuzu + cnt + 1);
cnt=unique(shuzu + 1,shuzu + cnt + 1) - shuzu - 1;
for(int i=1;i<=cnt;++i){
ans2+=s.gs(shuzu[i].first,shuzu[i].second);
}
printf("%d\n",ans2);
return 0;
}
C. 零一串
很妙的一道题。。。
考虑第个1如果能在第轮向前一步,那么第个1最早在轮向前一步
生成一个序列表示第个1在第轮是否能向前走一步
因为上面所说的性质,那么初始序列为的序列右移一位并且在开头补0
第个1和第个1之间有个0,就把操作序列的前个0改成1,解释一下大概是,当前这个1需要多走步才能赶上前面的那个1,然后跟在前面那个1后面走
关于逆序对,可以发现第轮的操作序列有几个1那么逆序对个数就相对上一轮加几,而分开考虑每个1,他的贡献是一个区间,那么差分维护就可以了
双端队列维护操作序列中0的位置,每加入一个1找出他对逆序对有贡献的区间,维护差分,最后对这个差分的前缀和进行前缀和可以得到该轮的逆序对数
实现不难,但是思路就难想到了
code
#include <cstring>
#include <cstdio>
#include <deque>
using namespace std;
const int maxn=5000005;
const int mod=998244353;
const int base=233;
char c[maxn];
int n,T,fr[maxn];
long long cf[maxn],nxd,gs1;
deque<int>q;
void pre_work(){
for(int i=1;i<=T;++i)q.push_back(i);
int ls=0;for(int i=n;i>=1;--i)if(c[i]=='0')++ls;else nxd+=ls,++gs1;
}
void work(){
int ls=0,ll=0;
for(int i=1;i<=n;++i)
if(c[i]=='1'){
++ls;q.push_front(1-ls);
while(!q.empty()&&q.back()+ls>T)q.pop_back();
for(int j=1;j<=ll;++j){
if(q.empty())break;
int x=q.front();
if(x+ls<maxn)++cf[x+ls];
if(gs1+x+1<maxn)--cf[gs1+x+1];
q.pop_front();
}
fr[i]=T-q.size();
ll=0;
}else ++ll;
}
void get_ans(){
long long dt=cf[0];
long long js=base;
long long ans=nxd%mod;
for(int i=1;i<=T;++i){
dt=dt+cf[i];
nxd=(nxd+dt)%mod;
long long ls=nxd*js%mod;
ans^=ls;
js=js*base%mod;
}
printf("%lld\n",ans);
}
void print_c(){
for(int i=1;i<=n;++i)
if(c[i]=='1'&&fr[i]){
c[i-fr[i]]='1';
c[i]='0';
}
for(int i=1;i<=n;++i)printf("%c",c[i]);
printf("\n");
}
int main(){
scanf("%d",&T);
scanf("%s",c+1);
n=strlen(c+1);
pre_work();
work();
print_c();
get_ans();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】