2018 uestc training for data structures(智商恢复训练)

集训前教练挂的一套题目。

教练说是基础题 em........

不得不佩服电子科大的训练题,质量是真的很高。


A 一颗简单的线段树


真简单的线段树  模板题

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int INF=1e9+7;
ll ans1,ans2,ans3;
int n,m,a[maxn];
struct node
{
    int l,r,mid;
    ll sum,mx,mi;
}t[maxn<<2];
void pushup(int rt)
{
    t[rt].sum=t[lson].sum+t[rson].sum;
    t[rt].mx=max(t[lson].mx,t[rson].mx);
    t[rt].mi=min(t[lson].mi,t[rson].mi);
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r;
    t[rt].mid=mid;
    if(l==r)
    {
        t[rt].mx=t[rt].mi=a[l];
        t[rt].sum=a[l];
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void update(int pos,int val,int rt)
{
    if(t[rt].l==t[rt].r)
    {
        t[rt].sum=t[rt].mx=t[rt].mi=val;
        return ;
    }
    if(pos<=t[rt].mid) update(pos,val,lson);
    else update(pos,val,rson);
    pushup(rt);
}
void query(int l,int r,int rt)
{
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        ans1+=t[rt].sum;
        ans2=max(ans2,t[rt].mx);
        ans3=min(ans3,t[rt].mi);
        return ;
    }
    if(l<=t[rt].mid) query(l,r,lson);
    if(r>t[rt].mid) query(l,r,rson);
}
int main()
{
    scanf("%d",&n);
    {
        build(1,n,1);
        scanf("%d",&m);
        while(m--)
        {
            int flag,l,r;
            scanf("%d%d%d",&flag,&l,&r);
            if(flag==0) update(l,r,1);
            if(flag==1)
            {
                ans1=0,ans2=-INF,ans3=INF;
                query(l,r,1);
                printf("%lld\n",ans1-ans2-ans3);
            }
        }
    }
}


B 一颗普通的线段树


真普通的线段树 就是区间更新的正常操作

需要注意 op的值是0或者其它   刚开始以为是 0和1 被坑了好久


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int INF=1e9+7;
ll ans;
int n,m;
struct node
{
    int l,r,mid;
    ll sum,lazy;
}t[maxn<<2];
void pushdown(int rt)
{
    if(t[rt].lazy!=0)
    {
        t[lson].sum+=t[rt].lazy*(t[lson].r-t[lson].l+1);
        t[lson].lazy+=t[rt].lazy;
        t[rson].sum+=t[rt].lazy*(t[rson].r-t[rson].l+1);
        t[rson].lazy+=t[rt].lazy;
        t[rt].lazy=0;
    }
}
void pushup(int rt)
{
    t[rt].sum=t[lson].sum+t[rson].sum;
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r;
    t[rt].mid=mid;
    if(l==r)
    {
        t[rt].lazy=0;
        t[rt].sum=0;
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void update(int l,int r,ll val,int rt)
{
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        t[rt].sum+=(val*(t[rt].r-t[rt].l+1));
        t[rt].lazy+=val;
        return ;
    }
    pushdown(rt);
    if(l<=t[rt].mid) update(l,r,val,lson);
    if(r>t[rt].mid) update(l,r,val,rson);
    pushup(rt);
}
void query(int l,int r,int rt)
{
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        ans+=t[rt].sum;
        return ;
    }
    pushdown(rt);
    if(l<=t[rt].mid) query(l,r,lson);
    if(r>t[rt].mid) query(l,r,rson);
    pushup(rt);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        build(1,n,1);
        while(m--)
        {
            int flag,l,r;
            ll v;
            scanf("%d%d%d%lld",&flag,&l,&r,&v);
            if(flag)
            {
                ans=0;
                query(l,r,1);
                printf("%lld\n",ans);
            }
            else update(l,r,v,1);
        }
    }
    //system("pause");
}


C 一颗像样的线段树


说实话有点难 = = 

刚开始自己想了好久没想出来怎么做 , 然后看了下题解。。。

其实我们可以发现b数组最多就是1到n,那么我们对于1到n开一颗线段树,每个结点储存当前数出现的最后的位置。因为我们要求的是 i-ci到i-1的范围内没有出现过的第一个数,那么我们现在储存了每个数出现的最后的位置,那么我们就找一个这个位置不在i-ci到i-1的范围内且最小的数即可。因为我们线段树是默认1到n即从小到大,所以即尽可能地往左子树找,明白这个方法之后线段树的实现很简单。


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn=1e6+5;
struct node
{
    int l,r,mid;
    int val;
}t[maxn<<2];
int n,c[maxn],b[maxn];
void pushup(int rt)
{
    t[rt].val=min(t[lson].val,t[rson].val);
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r,t[rt].mid=mid;
    if(l==r)
    {
        t[rt].val=-1;
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void update(int pos,int val,int rt)
{
    if(t[rt].l==t[rt].r)
    {
        t[rt].val=val;
        return ;
    }
    if(pos<=t[rt].mid) update(pos,val,lson);
    else update(pos,val,rson);
    pushup(rt);
}
int query(int k,int rt)
{
    if(t[rt].l==t[rt].r)
        return t[rt].l;
    if(t[lson].val<k) return query(k,lson); //尽可能往左查
    else return query(k,rson);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    build(1,n+1,1);
    update(1,0,1);  //1出现的最后位置是0
    for(int i=1;i<=n;i++)
    {
        b[i]=query(i-c[i],1);
        update(b[i],i,1);
    }
    for(int i=1;i<=n;i++)
        printf("%d%c",b[i],i==n?'\n':' ');
    //system("pause");
}



D 一颗复杂的线段树


这线段树 确实很复杂= = 

其实关键点在于如何实现l r区间的排序。死活想不到。。。

正解,二分答案,把比当前mid小的数置为0 大于等于mid的数置为1 ,然后区间的排序可以通过统计0 1的个数实现。最后check答案看是否符合要求

线段树的实现不是很复杂 思路真的难想。


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
int n,k,m,ans0,ans1,a[maxn];
struct Node
{
    int flag,l,r;
}q[maxn];
struct node
{
    int l,r,mid;
    int lazy,s1,s0;
}t[maxn<<2];
void pushdown(int rt)
{
    if(t[rt].lazy!=-1)
    {
        t[lson].lazy=t[rson].lazy=t[rt].lazy;
        if(t[rt].lazy)
        {
            t[lson].s0=0;
            t[lson].s1=t[lson].r-t[lson].l+1;
            t[rson].s0=0;
            t[rson].s1=t[rson].r-t[rson].l+1;
        }
        if(!t[rt].lazy)
        {
            t[lson].s1=0;
            t[lson].s0=t[lson].r-t[lson].l+1;
            t[rson].s1=0;
            t[rson].s0=t[rson].r-t[rson].l+1;
        }
        t[rt].lazy=-1;
    }
}
void pushup(int rt)
{
    t[rt].s0=t[lson].s0+t[rson].s0;
    t[rt].s1=t[lson].s1+t[rson].s1;
}
void build(int l,int r,int val,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r,t[rt].mid=mid;
    t[rt].lazy=-1;
    if(l==r)
    {
        t[rt].s0=t[rt].s1=0;
        if(a[l]>=val) t[rt].s1=1;
        else t[rt].s0=1;
        t[rt].lazy=-1;
        return ;
    }
    build(l,mid,val,lson);
    build(mid+1,r,val,rson);
    pushup(rt);
}
void update(int l,int r,int flag,int rt)
{
    if(l>r) return ;
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        t[rt].lazy=flag;
        if(flag)
        {
            t[rt].s0=0;
            t[rt].s1=t[rt].r-t[rt].l+1;
        }
        if(!flag)
        {
            t[rt].s1=0;
            t[rt].s0=t[rt].r-t[rt].l+1;
        }
        return ;
    }
    pushdown(rt);
    if(l<=t[rt].mid) update(l,r,flag,lson);
    if(r>t[rt].mid) update(l,r,flag,rson);
    pushup(rt);
}
bool query_1(int pos,int rt)    //判断当前点是否满足要求
{
    if(t[rt].l==t[rt].r)
    {
        if(t[rt].s1==1) return 1;
        return 0;
    }
    pushdown(rt);
    if(pos<=t[rt].mid) return query_1(pos,lson);
    if(pos>t[rt].mid) return query_1(pos,rson);
    pushup(rt);
}
void query(int l,int r,int rt)   //查询l r 0 1 个数
{
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        ans0+=t[rt].s0;
        ans1+=t[rt].s1;
        return ;
    }
    pushdown(rt);
    if(l<=t[rt].mid) query(l,r,lson);
    if(r>t[rt].mid) query(l,r,rson);
    pushup(rt);
}
bool check(int x)   //二分答案
{
    build(1,n,x,1);
    for(int i=1;i<=m;i++)
    {
        ans0=ans1=0;
        query(q[i].l,q[i].r,1);
        if(q[i].flag==0)    //通过区间更新实现区间排序
        {
            update(q[i].l,q[i].l+ans0-1,0,1);
            update(q[i].l+ans0,q[i].r,1,1);
        }
        if(q[i].flag==1)
        {
            update(q[i].l,q[i].l+ans1-1,1,1);
            update(q[i].l+ans1,q[i].r,0,1);
        }
    }
    return query_1(k,1);
}
int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++) scanf("%d%d%d",&q[i].flag,&q[i].l,&q[i].r);
        int L=1,R=n,ans=1;
        while(L<=R)
        {
            int mid=(L+R)>>1;
            if(check(mid)) ans=mid,L=mid+1;
            else R=mid-1;
        }
        printf("%d\n",ans);
    }
    //system("pause");
}


E 小埋的steam愿望单


其实就是把string离散一下即可,但是这里的不合法情况较多,例如没有加入

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
int n,ans,a[maxn];
string name;
vector<string> v;
int getid(string x) { return lower_bound(v.begin(),v.end(),x)-v.begin()+1; }
struct Node
{
    string name;
    int flag,val;
}q[maxn];
struct node
{
    int l,r,mid,flag,mx,mi;
    string namex,namei;
}t[maxn<<2];
void pushup(int rt)
{
    if(t[lson].mx>=t[rson].mx) t[rt].namex=t[lson].namex,t[rt].mx=t[lson].mx;
    else t[rt].namex=t[rson].namex,t[rt].mx=t[rson].mx;
    if(t[lson].mi<=t[rson].mi) t[rt].namei=t[lson].namei,t[rt].mi=t[lson].mi;
    else t[rt].namei=t[rson].namei,t[rt].mi=t[rson].mi;
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r,t[rt].mid=mid;
    t[rt].mx=-1,t[rt].mi=INF,t[rt].flag=0;
    if(l==r) return ;
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void update(int pos,int val,int flag,string name,int rt)
{
    if(t[rt].l==t[rt].r)
    {
        if(flag==-1)
        {
            t[rt].mi=INF,t[rt].mx=-1,t[rt].flag=0; //删除
            return ;
        }
        if(flag==0&&t[rt].flag==0) return ; //不存在不能更新
        if(flag==1&&t[rt].flag==1) return ; //已经存在 不能添加
        t[rt].flag=1;
        t[rt].mx=t[rt].mi=val;
        t[rt].namex=t[rt].namei=name;
        return ;
    }
    if(pos<=t[rt].mid) update(pos,val,flag,name,lson);
    if(pos>t[rt].mid) update(pos,val,flag,name,rson);
    pushup(rt);
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        v.clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&q[i].flag);
            if(q[i].flag==1) cin>>q[i].name>>q[i].val,v.push_back(q[i].name);
            if(q[i].flag==2) cin>>q[i].name,v.push_back(q[i].name);
            if(q[i].flag==3) cin>>q[i].name>>q[i].val,v.push_back(q[i].name);
            if(q[i].flag==4) cin>>q[i].val;
        }
        sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end()); //离散化
        build(1,v.size()+1,1);
        for(int i=1;i<=n;i++)
        {
            if(q[i].flag==1) update(getid(q[i].name),q[i].val,1,q[i].name,1);
            if(q[i].flag==2) update(getid(q[i].name),-1,-1,q[i].name,1);
            if(q[i].flag==3) update(getid(q[i].name),q[i].val,0,q[i].name,1);
            if(q[i].flag==4)
            {
                if(q[i].val==1)
                {
                    ans=INF;
                    if(t[1].mi==INF) continue;
                    cout<<t[1].namei<<endl;
                }
                if(q[i].val==2)
                {
                    ans=-1;
                    if(t[1].mx==-1) continue;
                    cout<<t[1].namex<<endl;
                }
            }
        }
    }
    //system("pause");
}

F 好吃不过饺子


题目读懂以后 其实就是暴力的线段树 分别存 最小值 最大值 总和即可,然后按照题意模拟

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn=1e5+5;
const int INF=1e9+7;
int n,c,ans1,ans2,ans3,a[maxn],b[maxn];
string s1,s2;
vector<int> v;
int getid(int x) { return lower_bound(v.begin(),v.end(),x)-v.begin()+1; }
struct node
{
    int l,r,mid;
    int sum,ma,mi;
}t[maxn<<2];
void pushup(int rt)
{
    t[rt].mi=min(t[lson].mi,t[rson].mi);
    t[rt].ma=max(t[lson].ma,t[rson].ma);
    t[rt].sum=t[lson].sum+t[rson].sum;
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r,t[rt].mid=mid;
    if(l==r)
    {
        t[rt].ma=t[rt].mi=t[rt].sum=b[l];
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void query(int l,int r,int rt)
{
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        ans1=max(ans1,t[rt].ma);
        ans2=min(ans2,t[rt].mi);
        ans3+=t[rt].sum;
        return ;
    }
    if(l<=t[rt].mid) query(l,r,lson);
    if(r>t[rt].mid) query(l,r,rson);
}
int main()
{
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]),v.push_back(a[i]);
    build(1,n,1);
    for(int i=1;i<=c;i++)
    {
        string s1,s2;
        int len,res=0;
        cin>>s1>>s2>>len;
        for(int j=1;j<=n;j++)
        {
            int L=getid(a[j]-len);  //注意一下左右的边界
            int R=getid(a[j])-1;
            ans1=-1,ans2=INF,ans3=0;
            if(s2=="min")
            {
                if(L>R) continue;
                else
                {
                    query(L,R,1);
                    if(s1=="gt"&&b[j]>ans2) res++;
                    if(s1=="lt"&&b[j]<ans2) res++; 
                }
            }
            if(s2=="max")
            {
                if(L>R) continue;                
                else
                {
                    query(L,R,1);
                    if(s1=="gt"&&b[j]>ans1) res++;
                    if(s1=="lt"&&b[j]<ans1) res++;
                }
            }
            if(s2=="avg")
            {
                if(L>R) continue;
                else 
                {
                    query(L,R,1);
                    if(s1=="gt"&&b[j]*(R-L+1)>ans3) res++;
                    if(s1=="lt"&&b[j]*(R-L+1)<ans3) res++;
                }
            }
        }
        printf("%d\n",res);
    }
    //system("pause");
}


G 三橙美琴的心里只有学习


简单的队列乱搞题 刚开始没看到输入的t是递增的  想了好久没想出来怎么做  后来看别人代码发现 默认按t的从小到大做的

仔细看了看题目发现真的有这个条件。。。

有这个条件的话 我们可以把每个项目的完工时间加入队列,每次询问前把所以不合法的全部pop掉即可。


#include <bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
int n,m;
int main()
{
    scanf("%d",&n);
    queue<pair<int,int>> que;
    for(int i=1;i<=n;i++)
    {
        int flag,t,a,b;
        scanf("%d%d",&flag,&t);
        while(!que.empty()&&que.front().second<=t) que.pop();
        if(flag==1)
        {
            scanf("%d%d",&a,&b);
            que.push({a,t+b});
        }
        if(flag==2) if(!que.empty()) que.pop();
        if(flag==3)
        {
            if(que.empty()) printf("-1\n");
            else printf("%d\n",que.front().first);
        }
    }
    //system("pause");
}


H 中堂系的困难任务

刚开始自己看着公式瞎搞了好久  怎么也跑不对答案,最后看题解发现是 哈夫曼树 ==   完全没看出来的我,  至今不知道怎么推的,求大佬教= = 

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
int n;
int main()
{
    int QAQ;
    scanf("%d",&QAQ);
    while(QAQ--)
    {
        scanf("%d",&n);
        priority_queue<ll,vector<ll>,greater<ll> > que;
        ll x;
        for(int i=1;i<=n;i++) scanf("%lld",&x),que.push(x);
        ll ans=0;
        while(que.size()>1)
        {
            ll a=que.top();
            que.pop();
            ll b=que.top();
            que.pop();
            ans+=(a+b);
            que.push(a+b);
        }
        printf("%lld\n",ans);
    }
    //system("pause");
}

I 不如把并查集加个计数功能吧

简单的并查集操作 多用一个数组记录每个联通块内的个数即可


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
int n,m,q,par[maxn],num[maxn];
void init()
{
    for(int i=0;i<=n;i++) par[i]=i;
    for(int i=0;i<=n;i++) num[i]=1;
}
int find(int x)
{
    if(par[x]==x) return x;
    else return par[x]=find(par[x]);
}
bool same(int x,int y)
{
    int fx=find(x),fy=find(y);
    if(fx==fy) return 1;
    return 0;
}
void unite(int x,int y)
{
    if(same(x,y)) return ;
    int fx=find(x),fy=find(y);
    par[fx]=fy;
    num[fy]+=num[fx];   //计数更新
}
int main()
{
    scanf("%d%d",&n,&m);
    init();
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        unite(x,y);
    }
    scanf("%d",&q);
    while(q--)
    {
        int x;
        scanf("%d",&x);
        printf("%d\n",num[find(x)]);
    }
    //system("pause");
}

J 老头马桶枪


同食物链的那道题目 几乎一样的操作,加权并查集。

#include <bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
int n,m,q,par[maxn],ra[maxn];
void init()
{
    for(int i=0;i<=n;i++) par[i]=i;
    for(int i=0;i<=n;i++) ra[i]=0;
}
int find(int x)
{
    if(par[x]==x) return x;
    else
    {
        int px=find(par[x]);
        ra[x]=(ra[par[x]]+ra[x])%3;
        par[x]=px;
    }
    return par[x];
}
bool same(int x,int y)
{
    int fx=find(x),fy=find(y);
    if(fx==fy) return 1;
    return 0;
}
void unite(int x,int y,int d)
{
    if(same(x,y)) return ;
    int fx=find(x),fy=find(y);
    par[fx]=fy;
    ra[fx]=(d+ra[y]-ra[x]+3)%3;
}
int main()
{
    scanf("%d%d",&n,&m);
    init();
    int res=0,ans=-1;
    while(m--)
    {
        int flag,x,y;
        scanf("%d%d%d",&flag,&x,&y);
        res++;
        if((x>n||y>n)&&ans==-1) ans=res%3;
        else if(flag==2&&x==y&&ans==-1) ans=res%3;
        else
        {
            if(same(x,y))
            {
                if((ra[x]-ra[y]+3)%3!=flag-1&&ans==-1) ans=res%3;
            }
            else unite(x,y,flag-1);
        }
    }
    if(ans==0) ans=3;
    printf("%d\n",ans);
    //system("pause");
}

K 爱吃瓜的伊卡洛斯(1)

西瓜只有两种 即非A即B  那么同样采用加权并查集即可。


#include <bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
int n,m,q,par[maxn],ra[maxn];
void init()
{
    for(int i=0;i<=n;i++) par[i]=i;
    for(int i=0;i<=n;i++) ra[i]=0;
}
int find(int x)
{
    if(par[x]==x) return x;
    else
    {
        int px=find(par[x]);
        ra[x]=(ra[par[x]]+ra[x])%2;
        par[x]=px;
    }
    return par[x];
}
bool same(int x,int y)
{
    int fx=find(x),fy=find(y);
    if(fx==fy) return 1;
    return 0;
}
void unite(int x,int y,int d)
{
    if(same(x,y)) return ;
    int fx=find(x),fy=find(y);
    par[fx]=fy;
    ra[fx]=(d+ra[y]-ra[x]+2)%2;
}
int main()
{
    scanf("%d%d",&n,&m);
    init();
    while(m--)
    {
        char c;
        int x,y,flag;
        scanf(" %c",&c);
        if(c=='A')
        {
            scanf("%d%d%d",&x,&y,&flag);
            unite(x,y,flag-1);
        }
        if(c=='Q')
        {
            int ans=0;
            scanf("%d%d",&x,&y);
            if(!same(x,y)) ans=3;
            else
            {
                if((ra[x]-ra[y]+2)%2==0) ans=1;
                else ans=2;   
            }
            printf("%d\n",ans);
        }
    }
    //system("pause");
}



L 爱吃瓜的伊卡洛斯(2)

这个题目将西瓜的种类数扩展到了无数种,瞬间就不会做了。。。

解法是采用启发式合并,不过也没搞懂启发式合并是什么鬼,感觉就是自己写的合并= = 

x y 如果是不同的瓜  那么我们可以用set维护与x不同种类的瓜,insert到set[x]中,同样的y中也要推入x。

当他们是同一种类的瓜是 将x和y连接,同时将他们的set合并,注意合并的时候可以只合并祖先来减少冗余。因为我们查找也是只查找祖先 所以问题不大。之后询问如果是同一并查集则种类相同 若在set可以找到则不相同 其他情况为未知。


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn=1e5+5;
const int INF=1e9+7;
int n,m,par[maxn];
char c;
set<int> s[maxn];
int find(int x)
{
    return par[x]==x? x : par[x]=find(par[x]);
}
void unite(int x,int y)
{
    int fx=find(x),fy=find(y);
    if(fx!=fy)
    {
        if(s[fx].size()>s[fy].size()) swap(fx,fy);
        par[fx]=fy; //并查集合并
        if(s[fx].size())    //跟x种类不同即跟y种类不同
        {
            for(auto it=s[fx].begin();it!=s[fx].end();it++)
            {
                s[fy].insert(find(*it));
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++) par[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y,flag;
        scanf(" %c",&c);
        if(c=='A')
        {
            scanf("%d%d%d",&x,&y,&flag);
            if(flag==1) unite(x,y);
            if(flag==2)
            {
                int fx=find(x),fy=find(y);
                if(fx!=fy)
                {
                    s[fx].insert(fy);
                    s[fy].insert(fx);
                }
            }
        }
        else
        {
            scanf("%d%d",&x,&y);
            int fx=find(x),fy=find(y);
            if(s[fx].find(fy)!=s[fx].end()||s[fy].find(fx)!=s[fy].end()) printf("2\n");
            else if(fx==fy) printf("1\n");
            else printf("3\n");
        }
    }
    //system("pause");
}

L 一道普通题1

这题根本一点不普通好吧 开始想着用线段树做 是真心不知道怎么做啊,后来瞄了一眼题解发现是分块= = 

跟我之前做的分块不是一个级别的 之前做的基本用线段树 都可以很简单的实现,这个线段树实现不了啊,当时学分块看qsc大佬的视频学的,专门用了解决一些瞎鸡儿更新和瞎鸡儿查询的题目 大概说的就是这些了= = 

用了分块其实就相当于是暴力算法 没啥好说的,

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
vector<int> s[1010];
int n,sz,pos[maxn],tag[maxn],a[maxn];
void add(int l,int r,int c)
{
    for(int i=l;i<=min(pos[l]*sz,r);i++) a[i]+=c;
    s[pos[l]].clear();  //对块进行重建
    for(int i=(pos[l]-1)*sz+1;i<=min(pos[l]*sz,n);i++) s[pos[i]].push_back(a[i]);
    sort(s[pos[i]].begin(),s[pos[i]].end());
    if(pos[l]==pos[r]) return ; //判断l r是否为同一块
    for(int i=pos[l]+1;i<=pos[r]-1;i++) tag[i]+=c;  //对块进行整体修改
    for(int i=(pos[r]-1)*sz+1;i<=r;i++) a[i]+=c;
    s[pos[r]].clear();
    for(int i=(pos[r]-1)*sz+1;i<=min(pos[r]*sz,n);i++) s[pos[i]].push_back(a[i]);
    sort(s[pos[i]].begin(),s[pos[i]].end());
}
int query(int l,int r,int c)
{
    int ans=-1;
    for(int i=l;i<=min(pos[l]*sz,r);i++)    //对于左右边界所在的块暴力搜索
        if(tag[pos[i]]+a[i]<c) ans=max(ans,tag[pos[i]]+a[i]);
    if(pos[l]==pos[r]) return ans;
    for(int i=(pos[r]-1)*sz+1;i<=r;i++)
        if(tag[pos[i]]+a[i]<c) ans=max(ans,tag[pos[i]]+a[i]);
    for(int i=pos[l]+1;i<=pos[r]-1;i++) //对中间块进行整体搜索
    {
        auto it=lower_bound(s[i].begin(),s[i].end(),c-tag[i]);
        if(it==s[i].begin()) continue;  //找不到跳过
        it--;
        ans=max(ans,*it+tag[i]);
    }
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        sz=sqrt(n*1.0);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pos[i]=(i-1)/sz+1;
            s[pos[i]].push_back(a[i]);
        }
        for(int i=1;i<=pos[n];i++) sort(s[i].begin(),s[i].end());
        for(int i=1;i<=n;i++)
        {
            int flag,l,r,c;
            scanf("%d%d%d%d",&flag,&l,&r,&c);
            if(flag==0) add(l,r,c);
            if(flag==1) printf("%d\n",query(l,r,c));
        }
    }
    //system("pause");
}

N 一道普通题2

这道题理论上应该是考察一个数被开方大概7次之后就是1这个知识点,不过我这里用了一种神奇的方法卡过去了,即对每一个结点打个标记,如果该叶结点的数被开方到1,那么就flag=1 如果区间flag都为1 则该区间flag=1  对于flag=1的区间之前返回区间结果即可。

这个方法理论上碰到那种0比较多的情况 应该时间会很慢 不过神奇的卡过去了

正常写线段树也可以,就是注意把0的情况考虑进去即可。

这里直接搬一年前的代码 比较丑= = 

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
typedef unsigned long long ULL;
const int maxn=5e4+5;
int n,m;
ll ans;
int a[maxn];
struct tree
{
    int l,r,mid;
    ll k;
    int cnt,flag;
    int mark;
}t[maxn<<2];
void pushdown(int rt)
{
    if(t[rt].mark==-1)
    {
        t[lson].cnt += t[rt].cnt;
        t[rson].cnt += t[rt].cnt;
        t[lson].mark=t[rson].mark=-1;
        t[rt].mark=t[rt].cnt=0;
    }
}
void pushup(int rt)
{
    t[rt].k=t[lson].k+t[rson].k;
    if(t[lson].flag&&t[rson].flag)
        t[rt].flag=1;
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].mid=mid;
    t[rt].l=l;t[rt].r=r;
    t[rt].cnt=t[rt].mark=t[rt].flag=0;
    if(l==r)
    {
        t[rt].k=a[t[rt].l];
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void update(int l,int r,int rt)
{
    if(t[rt].flag) return ;
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        t[rt].cnt++;
        t[rt].mark=-1;
        if(t[rt].l==t[rt].r)
        {
            for(int i=1;i<=t[rt].cnt;i++)
                t[rt].k=(int)sqrt(t[rt].k);
            t[rt].cnt=0;
            if(t[rt].k==1)
                t[rt].flag=1;
        }
        return ;
    }
    pushdown(rt);
    if(l<=t[rt].mid)
        update(l,r,lson);
    if(r>t[rt].mid)
        update(l,r,rson);
    pushup(rt);
}
void query(int l,int r,int rt)
{
    if(l<=t[rt].l&&t[rt].r<=r&&t[rt].flag)
    {
        ans=ans+t[rt].k;
        return ;
    }
    if(l<=t[rt].l&&t[rt].r<=r&&t[rt].l==t[rt].r)
    {
        for(int i=1;i<=t[rt].cnt;i++)
        t[rt].k=(int)sqrt(t[rt].k);
        t[rt].cnt=0;
        if(t[rt].k==1) t[rt].flag=1;
        ans=ans+t[rt].k;
        return ;
    }
    pushdown(rt);
    if(l<=t[rt].mid)
        query(l,r,lson);
    if(r>t[rt].mid)
        query(l,r,rson);
    pushup(rt);
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        build(1,n,1);
        int q,l,r,c;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d%d",&q,&l,&r,&c);
            if(q==0)
                update(l,r,1);
            if(q==1)
            {
                ans=0;
                query(l,r,1);
                printf("%lld\n",ans);
            }
        }
    }
}


O 帆宝RMQ

其实如果把M题做了的话 这题应该还好,采用的也是分块的思想,直接分块以后暴力查找即可,中间要注意块的重建。

我这里找最左和最右 直接用的lower_bound找到第一个数之后 依次找和它相同的数并不断的更新l r ,其实这样是会被某些数据卡掉的,一种很简单的做法其实就是从左往右找第一个x,从右往左找第一个x 依次采用lower_bound  和 upper_bound即可实现,两种方法的对比时间差异不大,可能数据较弱,这里给出第一种

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
struct node
{
    int val,idx;
    bool operator<(const node &p) const
    {
        if(val==p.val) return idx<p.idx;
        return val<p.val;
    }
}a[maxn];
vector<node> v[1010];
int n,q,sz,pos[maxn],tag[maxn];
void rebuild(int x)
{
    v[x].clear();
    for(int i=(x-1)*sz+1;i<=min(x*sz,n);i++)
        v[x].push_back(a[i]);
    sort(v[x].begin(),v[x].end());
}
void add(int l,int r,int c)
{
    for(int i=l;i<=min(pos[l]*sz,r);i++) a[i].val+=c;
    rebuild(pos[l]);
    if(pos[l]==pos[r]) return ;
    for(int i=pos[l]+1;i<pos[r];i++) tag[i]+=c;
    for(int i=(pos[r]-1)*sz+1;i<=r;i++) a[i].val+=c;
    rebuild(pos[r]);
}
int query(int x)
{
    int li=INF,ri=-INF;
    for(int i=1;i<=pos[n];i++)
    {
        node k;
        k.idx=0,k.val=x-tag[i];
        auto it=lower_bound(v[i].begin(),v[i].end(),k);
        if(it==v[i].end()) continue;    //没找到跳过
        while(it!=v[i].end())   //找到则依次暴力查找
        {
            if(it->val==k.val)
            {
                li=min(li,it->idx);
                ri=max(ri,it->idx);
            }
            else
                break;
            it++;
        }
    }
    if(li==INF||ri==-INF) return -1;
    return ri-li;
}
int main()
{
    scanf("%d%d",&n,&q);
    {
        sz=sqrt(n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i].val);
            a[i].idx=i;
            pos[i]=(i-1)/sz+1;
            v[pos[i]].push_back(a[i]);
        }
        for(int i=1;i<=pos[n];i++) sort(v[i].begin(),v[i].end());
        for(int i=1;i<=q;i++)
        {
            int flag,l,r,c;
            scanf("%d",&flag);
            if(flag==1)
            {
                scanf("%d%d%d",&l,&r,&c);
                add(l,r,c);
            }
            if(flag==2)
            {
                scanf("%d",&c);
                printf("%d\n",query(c));
            }
        }
    }
    //system("pause");
}

 P 为什么你这么熟练啊


这题目 刚开始我看着感觉跟主席树一模一样,主席数刚好每个结点存这个数在l r区间出现的次数,然后加了点自以为是的小优化就交了 光荣tle2,

看了题解是莫队,其实之前想到了莫队,但是没想到 l1 r1 l2 r2 这种有关联的区间怎么莫队。

结果被大佬折服了,l r x出现的次数 其实是1到r x出现次数减去 1 到 l-1 出现的次数。

这样原式子就可以转换为 ((1,r1)-(1,l1-1))*((1,r2)-(1,l2-1)),化简之后为

(1,r1)*(1,r2)-(1,r1)*(1,l2-1)-(1,l1-1)*(1,r2)+(1,l1-1)*(1,l2-1)。

这样就可以把原查询分为4个区间查询来写,这样分开以后就可以使用莫队了,把每个答案相应的贡献计算清楚即可。

具体看代码 有莫队基础应该很容易理解。


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn=5e4+5;
const int INF=1e9+7;
int n,q,a[maxn],pos[maxn],sl[maxn],sr[maxn],ans[maxn];
struct node //sl数组表示L移动是sl中x出现的次数 sr同理
{
    int l,r,flag,idx;
    bool operator<(const node &p) const
    {
        if(pos[l]==pos[p.l]) return r<p.r;
        return pos[l]<pos[p.l];
    }
};
vector<node> v;
int main()
{
    scanf("%d",&n);
    int sz=sqrt(n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),pos[i]=(i-1)/sz+1;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int l1,r1,l2,r2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);  //按照推的式子分为4个区间
        v.push_back({r1,r2,1,i});
        v.push_back({r1,l2-1,-1,i});
        v.push_back({l1-1,r2,-1,i});
        v.push_back({l1-1,l2-1,1,i});
    }
    sort(v.begin(),v.end());
    int L=0,R=0,res=0;
    for(auto it=v.begin();it!=v.end();it++)
    {
        while(L>it->l)  //这里L要-- 因为是乘法 L--之前 答案会减去 sr中a[L]的次数
        {               //之后的四步都同理
            res-=sr[a[L]];
            sl[a[L]]--;
            L--;
        }
        while(L<it->l)
        {
            L++;
            res+=sr[a[L]];
            sl[a[L]]++;
        }
        while(R>it->r)
        {
            res-=sl[a[R]];
            sr[a[R]]--;
            R--;
        }
        while(R<it->r)
        {
            R++;
            res+=sl[a[R]];
            sr[a[R]]++;
        }
        ans[it->idx]+=(it->flag*res);
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    //system("pause");
}


Q 这是一道简单题

真简单题 没啥说的


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int INF=1e9+7;
ll ans,a[maxn];
int n,m;
struct node
{
    int l,r,mid;
    ll sum,lazy;
}t[maxn<<2];
void pushdown(int rt)
{
    if(t[rt].lazy!=0)
    {
        t[lson].sum+=t[rt].lazy*(t[lson].r-t[lson].l+1);
        t[lson].lazy+=t[rt].lazy;
        t[rson].sum+=t[rt].lazy*(t[rson].r-t[rson].l+1);
        t[rson].lazy+=t[rt].lazy;
        t[rt].lazy=0;
    }
}
void pushup(int rt)
{
    t[rt].sum=t[lson].sum+t[rson].sum;
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r;
    t[rt].mid=mid;
    if(l==r)
    {
        t[rt].lazy=0;
        t[rt].sum=a[l];
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void update(int l,int r,ll val,int rt)
{
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        t[rt].sum+=(val*(t[rt].r-t[rt].l+1));
        t[rt].lazy+=val;
        return ;
    }
    pushdown(rt);
    if(l<=t[rt].mid) update(l,r,val,lson);
    if(r>t[rt].mid) update(l,r,val,rson);
    pushup(rt);
}
ll query(int pos,int rt)
{
    ll res=0;
    if(t[rt].l==t[rt].r)
    {
        return t[rt].sum;
    }
    pushdown(rt);
    if(pos<=t[rt].mid) res=query(pos,lson);
    if(pos>t[rt].mid) res=query(pos,rson);
    pushup(rt);
    return res;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        build(1,n,1);
        for(int i=1;i<=n;i++)
        {
            int flag,l,r;
            ll v;
            scanf("%d%d%d%lld",&flag,&l,&r,&v);
            if(flag) printf("%lld\n",query(r,1));
            else update(l,r,v,1);
        }
    }
    //system("pause");
}


R letter Kingdom

不会做!!!


这次的训练题教练说是基础 

太难了啊= = 

不过起到了很好的智商恢复作用










猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/81046958