NOIP提高组模拟赛23


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

A. d

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

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

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

code
#include
#include
#include
#include
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

B. e

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

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

没错,暴力跳!

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

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

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

暴力没有上界!!

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

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

code

#include
#include
#include
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]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

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

C. f

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

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

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

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

code
#include
#include
#include

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.numx&&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<>j&1];
        fr[i+1].i=i;
    }
    sort(fr+1,fr+s1+1,cmp);
    for(int i=0;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>1;
        if(check(mid)ans1)--j;
        int ls=j;
        while(se[j].num+fr[i].num==ans1&&j>0)a2[++p2]=fr[i].i+(se[j].i<