T1 联

$n$最大到$1e18$,根本没法做,但$m$只有$1e5$,发现有很多区间是一起动的,或者根本没动,所以可以把区间离散化掉,然后线段树区间修改,对于第三种修改,只需要把它分解成一段一段相同的区间,再区间覆盖就可以。

在线段树中维护一个$cnt$,表示区间中$0$的个数,在询问的时候,只需要找到最左端$cnt!=0$的地方,在把离散化后的映射回来输出即可。

要注意的一点是,不仅要每个区间的左右端点,也要把区间右端点$+1$的地方也离散化,留下可能是$0$的位置,并且把$1$位置扔进去,不然可能会找不到答案,离散化后是$3×m$个点,数组要开够。。。

#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
#define ll long long
using namespace std;
struct tree
{
    ll l,r,lz,cnt;//sz为区间0的个数
}t[1200100];
struct node
{
    ll opt,l,r;
}q[100100];
ll n,m,tot,sum,cp[300100],pos[300100];
map<ll,ll>mp;
ll read()
{
    ll aa=0,bb=1;char cc=getchar();
    while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
    while(cc>='0'&&cc<='9'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();}
    return aa*bb;
}
void build(ll x,ll l,ll r)
{
    t[x].l=l;t[x].r=r;t[x].cnt=t[x].r-t[x].l+1;
    if(l==r) return;
    ll mid=l+r>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
}
void down(ll x)
{
    if(t[x].lz==1){
        t[x<<1].cnt=0;t[x<<1|1].cnt=0;
        t[x<<1].lz=t[x].lz;t[x<<1|1].lz=t[x].lz;
        t[x].lz=0;
    }
    else if(t[x].lz==2){
        t[x<<1].cnt=t[x<<1].r-t[x<<1].l+1;t[x<<1|1].cnt=t[x<<1|1].r-t[x<<1|1].l+1;
        t[x<<1].lz=t[x].lz;t[x<<1|1].lz=t[x].lz;
        t[x].lz=0;
    }
}
void update(ll x)
{
    t[x].cnt=t[x<<1].cnt+t[x<<1|1].cnt;
}
void change(ll x,ll l,ll r,ll opt)//1:全改为1 2:全改为0 cnt:0的个数
{
    if(l<=t[x].l&&t[x].r<=r){
        if(opt==1){
            t[x].lz=opt;
            t[x].cnt=0;
            return;
        }
        if(opt==2){
            t[x].lz=opt;
            t[x].cnt=t[x].r-t[x].l+1;
            return;
        }
        if(opt==3){
            if(t[x].cnt==0){
                t[x].lz=2;
                t[x].cnt=t[x].r-t[x].l+1;
                return;
            }
            else if(t[x].cnt==t[x].r-t[x].l+1){
                t[x].lz=1;
                t[x].cnt=0;
                return;
            }
        }
    }
    down(x);
    ll mid=t[x].l+t[x].r>>1;
    if(l<=mid) change(x<<1,l,r,opt);
    if(r>mid) change(x<<1|1,l,r,opt);
    update(x);
}
ll query(ll x)
{
    if(t[x].l==t[x].r||t[x].cnt==t[x].r-t[x].l+1) return t[x].l;
    down(x);
    if(t[x<<1].cnt) return query(x<<1);
    else return query(x<<1|1);
}
int main()
{
    n=read();cp[++tot]=1;
    for(int i=1;i<=n;i++) q[i].opt=read(),q[i].l=read(),q[i].r=read(),cp[++tot]=q[i].l,cp[++tot]=q[i].r,cp[++tot]=q[i].r+1;
    sort(cp+1,cp+tot);
    for(int i=1;i<=tot;i++) if(!mp[cp[i]]) mp[cp[i]]=++sum,pos[sum]=cp[i];
    for(int i=1;i<=n;i++) q[i].l=mp[q[i].l],q[i].r=mp[q[i].r];
    build(1,1,sum);
    for(int i=1;i<=n;i++){
        change(1,q[i].l,q[i].r,q[i].opt);
        printf("%lld\n",pos[query(1)]);
    }
    return 0;
}

T2 赛

把$n$个物品分成$4$类,甲乙都喜欢,只有甲喜欢,只有乙喜欢,甲乙都不喜欢,并按权值$sort$

枚举选$i$个甲乙都喜欢的,那么就要最小的$k-i$个甲喜欢的,最小的$k-i$个乙喜欢的,然后还不够的就从剩下的没选的里面选$m-k-k+i$个最小的,但显然会超时

把权值离散化,插到一棵权值线段树里,每次查询最小的$m-k-k+i$个元素的和

随着$i$的递推,新加入的点只有两个,但要注意边界问题,因为这个wa95了一晚上

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<set>
#include<map>
#define ll long long
using namespace std;
struct tree
{
    ll l,r,cnt,sum;
}t[8000100];
ll n,m,k,x,y;
ll ans,a[200100],q0[200100],q1[200100],q2[200100],cp[200100],rt,tot,num,id[200100];
bool aa[200100],bb[200100];
vector<ll>ve[4];
map<ll,ll>mp;
ll read()
{
    ll aa=0,bb=1;char cc=getchar();
    while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
    while(cc>='0'&&cc<='9'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();}
    return aa*bb;
}
void insert(ll &x,ll l,ll r,ll now)
{
    if(!x) x=++num;
    t[x].cnt++;t[x].sum+=id[now];
    if(l==r) return;
    ll mid=l+r>>1;
    if(now<=mid) insert(t[x].l,l,mid,now);
    else insert(t[x].r,mid+1,r,now);
}
ll query(ll x,ll l,ll r,ll cnt)
{
    if(t[x].cnt<=cnt) return t[x].sum;
    if(l==r) return l*cnt;
    ll mid=l+r>>1,as=0;
    as+=query(t[x].l,l,mid,cnt);
    if(t[t[x].l].cnt<cnt) as+=query(t[x].r,mid+1,r,cnt-t[t[x].l].cnt);
    return as;
}
int main()
{
    //freopen("b19.in","r",stdin);
    n=read();m=read();k=read();ans=0x7ffffffffffff;
    for(int i=1;i<=n;i++) a[i]=read(),cp[i]=a[i];
    x=read();
    for(int i=1;i<=x;i++) aa[read()]=1;
    y=read();
    for(int i=1;i<=y;i++) bb[read()]=1;
    if(x<k||y<k||k>m){
        puts("-1");
        return 0;
    }
    sort(cp+1,cp+n+1);
    for(int i=1;i<=n;i++) if(!mp[cp[i]]) mp[cp[i]]=++tot,id[tot]=cp[i];
    for(int i=1;i<=n;i++){
        if(aa[i]&&bb[i]) ve[0].push_back(a[i]);
        else if(aa[i]&&!bb[i]) ve[1].push_back(a[i]);
        else if(!aa[i]&&bb[i]) ve[2].push_back(a[i]);
        else if(!aa[i]&&!bb[i]) ve[3].push_back(a[i]);
    }
    sort(ve[0].begin(),ve[0].end());
    sort(ve[1].begin(),ve[1].end());
    sort(ve[2].begin(),ve[2].end());
    for(int i=0;i<ve[0].size();i++) q0[i+1]=q0[i]+ve[0][i];
    for(int i=0;i<ve[1].size();i++) q1[i+1]=q1[i]+ve[1][i];
    for(int i=0;i<ve[2].size();i++) q2[i+1]=q2[i]+ve[2][i];
    for(int i=0;i<ve[3].size();i++) insert(rt,1,n,mp[ve[3][i]]);
    for(int i=k+1;i<ve[1].size();i++) insert(rt,1,n,mp[ve[1][i]]);
    for(int i=k+1;i<ve[2].size();i++) insert(rt,1,n,mp[ve[2][i]]);
    for(int i=0;i<=min((ll)k,(ll)ve[0].size());i++){
        if(k-i<ve[1].size()) insert(rt,1,n,mp[ve[1][k-i]]);
        if(k-i<ve[2].size()) insert(rt,1,n,mp[ve[2][k-i]]);
        if(m-k-k+i<0||k-i>=ve[1].size()||k-i>=ve[2].size()) continue;
        ll as=q0[i]+q1[k-i]+q2[k-i];
        as+=query(rt,1,n,m-k-k+i);
        ans=min(ans,as);
    }
    if(ans>=2251799813685247){
        puts("-1");
        return 0;
    }
    printf("%lld\n",ans);
    return 0;
}

T3 题

$f[i]$表示$i$这个苹果是否可能剩下,$0$表示不可能,$1$可能剩下,初始全为$1$,$g[i][j](bitset)$表示i这个苹果必须有,最终的状态,$1$表示状态已经确定,$0$表示不确定

枚举每个苹果,让他必须活下来,倒序枚举$m$个人,看最后一个人要吃的苹果,

如果$a$,$b$都不确定,那么他们还是都不确定,

如果$a$确定,那么$j$这个人就只能吃$b$,所以$b$也确定了

如果$b$确定,那么$j$这个人就只能吃$a$,所以$a$也确定了

如果$a$,$b$都已确定,那这个状态就推不回去了(后边的人把他确定了,但他在后边的人前边,所以一定是他先吃,状态一定不合法),所以i一定不会活下来

最后再枚举每个苹果,如果他可能剩下,再去找另一个可能剩下的苹果,看他们两个最终状态是不是交集为空(一个苹果只能被一个人吃),为空$ans++$

#include<iostream>
#include<cstdio>
#include<bitset>
using namespace std;
int n,m,ans,a[50010],b[50010];
bitset<410>g[410],as;
bool f[410];
int read()
{
    int aa=0,bb=1;char cc=getchar();
    while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
    while(cc>='0'&&cc<='9'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();}
    return aa*bb;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++) a[i]=read(),b[i]=read();
    for(int i=1;i<=n;i++) f[i]=1;
    for(int i=1;i<=n;i++){
        g[i][i]=1;
        for(int j=m;j>=1;j--){
            if(g[i][a[j]]&&g[i][b[j]]){f[i]=0;break;}
            else if(g[i][a[j]]&&!g[i][b[j]]) g[i][b[j]]=1;
            else if(!g[i][a[j]]&&g[i][b[j]]) g[i][a[j]]=1;
        }
    }
    for(int i=1;i<=n;i++){
        if(f[i]){
            for(int j=i+1;j<=n;j++){
                if(f[j]){
                    as=g[i]&g[j];
                    if(!as.count()) ans++;
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

不想联赛就退役就静下来好好想

01-23 14:32