NOIP提高组模拟赛13

A. 送花

考虑选择一段以i为结尾的区间,记录las[i]为上一个与c[i]颜色相同的花的位置,发现只有在las[i]+1i这个区间才会有d[c[i]]的贡献,而之前有贡献的pos[pos[i]]+1pos[i],没有了d[c[i]]的贡献,用线段树维护区间最值,支持区间修改即可

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. 星空

发现在y=x+ky=x+k这条直线上的点直接距离为0,两点直接距离可以是一个点向另一个点的那两条直线作垂线的垂线段长度,考场想到这里就想不下去了,最后还是打的暴力最短路。。

旋转坐标轴,为y=xy=x,计算新的坐标(xy,x+y)

image

两点的直接距离就变成min(abs(xxxy),abs(yxyy))

距离为0的点可以看做一个集合,两个集合点对数就是两个集合size相乘,两个集合中任意两点最短距离就是集合中直接距离最小的两个点的距离

并查集维护集合,距离不为零尝试更新最短距离

算出最短距离后枚举为最短距离的两点,找到他们所在的集合,记录

对所有记录的两个集合去重,统计答案即可

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. 零一串

很妙的一道题。。。

考虑第i个1如果能在第t轮向前一步,那么第i+1个1最早在t+1轮向前一步

生成一个序列表示第i个1在第j轮是否能向前走一步

因为上面所说的性质,那么初始序列为i1的序列右移一位并且在开头补0

i1个1和第i个1之间有x个0,就把操作序列的前x个0改成1,解释一下大概是,当前这个1需要多走x步才能赶上前面的那个1,然后跟在前面那个1后面走

关于逆序对,可以发现第x轮的操作序列有几个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;
}

posted @   Chen_jr  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示