ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NOIP提高组模拟赛23

2022-05-14 19:31:26  阅读:146  来源: 互联网

标签:return val NOIP 23 int maxn ans include 模拟


本场比赛是暴力的胜利!!!!

A. d

怎么说呢,考场想到正解方式,但是错误使用双指针,而且完全没想平衡树。。。。

做法就是分别按照\(a,b\)排序,然后枚举按照升序删掉多少\(a\)小的,剩下的按照\(b\)删,使用平衡树很好的解决,可惜考场完全忘了有这个东西。。

好在这样的暴力有\(81pts\)

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;

typedef long long ll;
const int maxn=100005;
const int inf=2147483647;
int n,m;
struct node{ll a,b;}a[maxn];
struct FHQ_Treap{
	struct node{
		int l,r,val,size,key;
	}t[maxn];
	int root,tot;
	int New(int val){t[++tot].val=val;t[tot].size=1;t[tot].key=rand();return tot;}
	void push_up(int x){t[x].size=t[t[x].l].size+t[t[x].r].size+1;}
	void split(int x,int &l,int &r,int val){
		if(x==0){l=r=0;return;}
		if(t[x].val>val){split(t[x].l,l,t[x].l,val);r=x;push_up(r);return;}
		else{split(t[x].r,t[x].r,r,val);l=x;push_up(l);return;}
	}
	int merge(int l,int r){
		if(!l||!r)return l|r;
		if(t[l].key<t[r].key){t[l].r=merge(t[l].r,r);push_up(l);return l;}
		else{t[r].l=merge(l,t[r].l);push_up(r);return r;}
	}
	void insert(int val){
		int l=0,r=0;split(root,l,r,val);
		root=merge(merge(l,New(val)),r);
	}
	void erase(int val){
		int l=0,r=0,m=0;split(root,l,r,val);split(l,l,m,val-1);
		m=merge(t[m].l,t[m].r);
		root=merge(merge(l,m),r);
	}
	int kth(int x,int k){
		if(k==t[t[x].l].size+1)return t[x].val;
		if(k<=t[t[x].l].size)return kth(t[x].l,k);
		else return kth(t[x].r,k-t[t[x].l].size-1);
	}
	void pre(int x){
        x+=10;
        for(int i=1;i<=x;++i)t[i].val=t[i].l=t[i].r=t[i].size=0;
        root=0;tot=0;
    }
}T;
int pa[maxn];
bool cma(int x,int y){
    if(a[x].a!=a[y].a)return a[x].a<a[y].a;
    return a[x].b<a[y].b;
}

void work(){
    T.pre(n);
    for(int i=1;i<=n;++i)pa[i]=i;
    sort(pa+1,pa+n+1,cma);
    for(int i=1;i<=n;++i)T.insert(a[i].b);
    ll ans=T.kth(T.root,m+1)*1ll*a[pa[1]].a;
    for(int i=1;i<=m;++i){
        T.erase(a[pa[i]].b);
        ans=max(ans,T.kth(T.root,m-i+1)*a[pa[i+1]].a*1ll);
    }
    printf("%lld\n",ans);
}

int main(){
    int T;scanf("%d",&T);
    for(int ask=1;ask<=T;++ask){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)scanf("%lld%lld",&a[i].a,&a[i].b);
        work();       
    }
    return 0;
}

B. e

这是暴力的高光时刻!!!!

每次询问按照\(dfs\)序排一下,然后对相邻的两个点不停的暴力跳父亲取\(min\)

没错,暴力跳!

这样有\(89pts\)!!!

\(pyt\)学长在博客里说暴力的上界是\(89\)

然而,只需要加上快读+快输+特判(\(val-r==0\)),你就能轻松愉快的\(A\)掉,什么主席树?树套树?暴力\(yyds\)!!!!

暴力没有上界!!

其实应该可以卡掉,但是数据有点水,可以证明这种暴力的复杂度在最坏情况下是询问次数*节点数

假设树退化成一条链,每次都询问两个端点.......那这个暴力就萎了

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=100005;
int n,a[maxn],head[maxn],tot,lastans,ls[maxn];
bool flag;
int read(){
    int x=0;char c;c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}

void print(int x){
    if(x==0)putchar('0');
    char c[105];int w=0;
    while(x)c[++w]=x%10+'0',x/=10;
    for(int i=w;i>=1;--i)putchar(c[i]);
    putchar('\n');
}
struct edge{
    int to,net;
}e[maxn<<1|1];
void add(int u,int v){
    e[++tot].net=head[u];
    head[u]=tot;
    e[tot].to=v;
}
struct node{
    int dep,fa,son,top,size,id;
}d[maxn];
int dfn[maxn],dn;

void dfs1(int x){
    dfn[x]=++dn;
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==d[x].fa)continue;
        d[v].dep=d[x].dep+1;
        d[v].fa=x;
        dfs1(v);
    }
}

bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}

int work(int u,int v,int r){
    int ans=2147483647;
    if(d[u].dep<d[v].dep)swap(u,v);
    while(d[u].dep>d[v].dep){
        ans=min(ans,abs(a[u]-r));
        if(ans==0)return ans;
        u=d[u].fa;
    }
    while(u!=v){
        ans=min(ans,abs(a[u]-r));
        ans=min(ans,abs(a[v]-r));
        if(ans==0)return ans;
        u=d[u].fa;v=d[v].fa;
    }
    ans=min(ans,abs(a[u]-r));
    return ans;
}

void DO(int k,int r){
    if(k<=1||flag){
        lastans=abs(a[ls[1]]-r);
        return;
    }
    sort(ls+1,ls+k+1,cmp);
    lastans=2147483647;
    for(int i=1;i<k;++i)lastans=min(lastans,work(ls[i],ls[i+1],r));
}
int main(){
    
    int q,type;flag=1;
    n=read();q=read();type=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<=n;++i)if(a[i]!=a[1]){flag=0;break;}
    for(int i=1;i<n;++i){
        int u,v;u=read();v=read();
        add(u,v);add(v,u);
    }
    if(!flag){
        d[1].dep=1;
        dfs1(1);
    }
    
    for(int i=1;i<=q;++i){
        int r,k;r=read();k=read();
        for(int j=1;j<=k;++j)ls[j]=read();
        for(int j=1;j<=k;++j)ls[j]=(ls[j]-1+lastans*type)%n+1;
        DO(k,r);
        print(lastans);
    }
    return 0;
}

不过这题还是再次证明了那个真理:暴力出奇迹!

C. f

两个数是否对逆序对有贡献,取决于两个数二进制不同的最高位

用\(Tire\)树预处理出来从某位开始与该数不同的数的个数,然后二分答案,二分逆序对个数,查询有多少小于的

直接搞会\(TLE\),需要\(meet in middle\),因为每一位的贡献是固定且互不影响的,所以将二进制拆成两半考虑,每次组合一下即可

查询时用到了一点双指针思想,利用前一半数量单增,对应后一半单减进行优化。

code
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long ll;

const int maxn=500005;
int a[maxn],a2[maxn],p2;
ll gt[33][2];
int n,k,p,k1,k2,s2,s1,root;
struct tire{
    struct node{
        int son[2],cnt;
    }t[maxn<<6|1];
    int cnt=1;
    void ins(int v){
        int x=1;
        for(int i=30;i>=-1;--i){
            ++t[x].cnt;
            if(i==-1)break;
            int now=v>>i&1;
            if(!t[x].son[now])t[x].son[now]=++cnt;
            gt[i][now]+=t[t[x].son[now^1]].cnt;
            x=t[x].son[now];
        }
    }
}T;
struct node{
    ll num;int i;
}fr[maxn],se[maxn];
bool cmp(node x,node y){return x.num<y.num;}
int check(ll x){
    ll ans=0,j=s2;
    for(int i=1;i<=s1;++i){
        while(se[j].num+fr[i].num>x&&j>0)--j;
        ans+=j;
    }
    return ans;
}

int main(){
    scanf("%d%d%d",&n,&k,&p);   
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    if(k==0){printf("0 0\n");return 0;}
    for(int i=1;i<=n;++i)T.ins(a[i]);
    k1=k>>1;k2=k-k1;
    s1=1<<k1,s2=1<<k2;
    for(int i=0;i<s1;++i){
        for(int j=0;j<k1;++j)fr[i+1].num+=gt[j][i>>j&1];
        fr[i+1].i=i;
    }
    sort(fr+1,fr+s1+1,cmp);
    for(int i=0;i<s2;++i){
        for(int j=0;j<k2;++j)se[i+1].num+=gt[j+k1][i>>j&1];
        se[i+1].i=i;
    }
    sort(se+1,se+s2+1,cmp);
    ll l=0,r=1ll*n*(n-1)/2;
    while(l<r){
        ll mid=(l+r)>>1;
        if(check(mid)<p)l=mid+1;
        else r=mid;
    }
    ll ans1=l;
    ll pr=check(ans1-1),j=s2;
    for(int i=1;i<=s1;++i){
        while(se[j].num+fr[i].num>ans1)--j;
        int ls=j;
        while(se[j].num+fr[i].num==ans1&&j>0)a2[++p2]=fr[i].i+(se[j].i<<k1),--j;
        j=ls;
    }
    sort(a2+1,a2+p2+1);
    ll ans2=a2[p-pr];
    printf("%lld %lld\n",ans1,ans2);
    return 0;
}

标签:return,val,NOIP,23,int,maxn,ans,include,模拟
来源: https://www.cnblogs.com/Chencgy/p/16271082.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有