SEERC 2020

链接

A. Archeologists

考虑前缀和,这样相当于每个位置有一个权值 xi,要填一个合法的括号序列(可以填空格),左括号对应加,右括号对应减,问最大权值,可以用线段树模拟费用流或者闵可夫斯基和处理。

实际上这是一个经典问题:假设我们强制取 n 个区间,只不过允许取空区间。由于每个点只能作为左端点或右端点一次,不妨认为空格是既取右端点又取左端点,考虑从后往前处理,先假设当前点作为左端点匹配,此时会取出堆中剩余最大的右端点(如果此时匹配价值为负就不匹配了),然后再将其作为右端点塞入。

同时,有可能出现退流的情况,所以如果当前点作为左端点匹配时,额外将当前点作为退流时的右端点匹配。

复杂度 O(nlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define N 100010
#define ll long long
using namespace std;
ll s[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&s[i]),s[i]+=s[i-1];
    priority_queue<ll>q;q.push(s[n]);
    ll ans=0;
    for(int i=n-1;i>=0;i--)
    {
        if(q.top()>s[i]) ans+=q.top()-s[i],q.pop(),q.push(s[i]);
        q.push(s[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

C. 3-colorings

不会

D. Disk Sort

容易发现总是能找到一个没有排序的颜色,有一个在顶上并且至多只有一个在最后一层。直接分讨即可。

F. Fence Job

考虑一个位置可能填什么数。显然一个位置如果左边的数小于它,那么再往左左边的数一定不能等于它。如果右边的数小于它,那么再往右的数也不能等于它。

可以找到一个数能够覆盖的左端点和右端点 [l,r]。对这段区间内的值进行转移即可。

复杂度 O(n2)

G. Simple Hull

不会

H. AND = OR

看起来 OR 和 AND 的限制很小,但实际上是很严苛的。因为两个数的或一定不小于任何一个数,两个数的与一定不大于任何一个数。换句话说,假设最后结果为 x,所有 >x 的一定分到与集合,所有 <x 的一定分到或集合。

这样从左往右找到每个二进制位第一个 1 的位置和最后一个 0 的位置,只对这些位置判断是否合法即可。复杂度 O(nlognlogV)

事实上可以再分析一下,可以发现如果 x 二进制位数为 k,那么所有 <k 的数字必须分给或集合,>k 的数字必须分给与集合。=k 的数字如果不全相等,一定无解,否则分讨一下划给哪一组。

复杂度也是 O(n(logn+logV))

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#define fi first
#define se second
using namespace std;
const int N=100010,inf=2147483647;
int a[N],n;
typedef pair<int,int> pi;
struct seg_tree{
    int mx[N<<2],mn[N<<2];
    int cmp_min(int x,int y){return (a[x]<a[y] || (a[x]==a[y] && x<y))?x:y;}
    int cmp_max(int x,int y){return (a[x]>a[y] || (a[x]==a[y] && x>y))?x:y;}
    void build(int u,int l,int r,int k)
    {
        if(l==r){if(a[l]>>k&1) mn[u]=l,mx[u]=0;else mx[u]=l,mn[u]=n+1;return;}
        int mid=(l+r)>>1;
        build(u<<1,l,mid,k),build(u<<1|1,mid+1,r,k);
        mx[u]=cmp_max(mx[u<<1],mx[u<<1|1]),mn[u]=cmp_min(mn[u<<1],mn[u<<1|1]);
    }
    int qry_min(int u,int l,int r,int L,int R)
    {
        if(L<=l && r<=R) return mn[u];
        int mid=(l+r)>>1;
        if(R<=mid) return qry_min(u<<1,l,mid,L,R);
        if(L>mid) return qry_min(u<<1|1,mid+1,r,L,R);
        return cmp_min(qry_min(u<<1,l,mid,L,R),qry_min(u<<1|1,mid+1,r,L,R));
    }
    int qry_max(int u,int l,int r,int L,int R)
    {
        if(L<=l && r<=R) return mx[u];
        int mid=(l+r)>>1;
        if(R<=mid) return qry_max(u<<1,l,mid,L,R);
        if(L>mid) return qry_max(u<<1|1,mid+1,r,L,R);
        return cmp_max(qry_max(u<<1,l,mid,L,R),qry_max(u<<1|1,mid+1,r,L,R));
    }
}t[31];
int t0[N<<2],sm[N<<2];
int cmp(int x,int y){return a[x]<a[y]?x:y;}
void build(int u,int l,int r){if(l==r){t0[u]=l,sm[u]=a[l];return;}int mid=(l+r)>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);t0[u]=cmp(t0[u<<1],t0[u<<1|1]),sm[u]=sm[u<<1]&sm[u<<1|1];}
int qry(int u,int l,int r,int L,int R)
{
    if(L<=l && r<=R) return t0[u];
    int mid=(l+r)>>1;
    if(R<=mid) return qry(u<<1,l,mid,L,R);
    if(L>mid) return qry(u<<1|1,mid+1,r,L,R);
    return cmp(qry(u<<1,l,mid,L,R),qry(u<<1|1,mid+1,r,L,R));
}
int sum(int u,int l,int r,int L,int R)
{
    if(L<=l && r<=R) return sm[u];
    int mid=(l+r)>>1;
    if(R<=mid) return sum(u<<1,l,mid,L,R);
    if(L>mid) return sum(u<<1|1,mid+1,r,L,R);
    return sum(u<<1,l,mid,L,R)&sum(u<<1|1,mid+1,r,L,R);
}
vector<pi>p;
int main()
{
    int m;scanf("%d%d",&n,&m);
    a[0]=-inf,a[n+1]=inf;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=0;i<31;i++) t[i].build(1,1,n,i);
    build(1,1,n);
    while(m --> 0)
    {
        int l,r;scanf("%d%d",&l,&r);
        int va=0,vb=0;p.clear();
        for(int i=0;i<31;i++)
            p.push_back({t[i].qry_min(1,1,n,l,r),i});
        int wa=0,wb=0;
        for(int i=0;i<31;i++)
            p.push_back({t[i].qry_max(1,1,n,l,r),-i-1}),wb++;
        {
            int pos=qry(1,1,n,l,r),w=inf;
            if(a[pos]==0)
            {
                if(pos!=l) w&=sum(1,1,n,l,pos-1);
                if(pos!=r) w&=sum(1,1,n,pos+1,r);
            }
            if(w==0){puts("YES");continue;}
        }
        sort(p.begin(),p.end(),[&](pi x,pi y){return a[x.fi]==a[y.fi]?x.fi<y.fi:a[x.fi]<a[y.fi];});
        bool hv=false;int pre=-1;
        for(auto [c,v]:p)
        {
            if(pre!=c && wa && wb && va==vb){hv=true;break;}
            pre=c;
            if(v>=0) va|=1<<v,wa++;
            else v=-v-1,vb|=1<<v,wb--;
        }
        puts(hv?"YES":"NO");
    }
    return 0;
}

J. One Piece

考虑如果找出所有宝藏构成的虚树,考虑其直径的中点,一定对应所有距离中最小的点(如果有两个就边上新建一个点),设其为 u。容易证明只要满足 u 所有子树内对应 au 深度位置有宝藏,且 >au 位置没有宝藏,就一定合法。

所有点可以分为按深度分为 <au,=au,>au 三个部分。<au 概率就是 0.5=au 部分概率大于 0.5,且由于每个子树内至少有一个,所以子树内该深度点越多,概率越低。>au 概率是 0

直接排序即可。复杂度 O(nlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=250010;
vector<int>g[N];
int r1,r2,md,fr[N],x[N],y[N],f[N],id[N];
int a[N],siz[N],dep[N];
void dfs0(int u,int p)
{
    siz[u]=dep[u]==md;
    if(fr[p]) fr[u]=fr[p];
    else if(p) fr[u]=u;
    for(int v:g[u]) if(v!=p)
        dep[v]=dep[u]+1,dfs0(v,u),siz[u]+=siz[v];
}
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<n;i++) scanf("%d%d",&x[i],&y[i]);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    md=n;
    for(int i=1;i<=n;i++) if(a[i]<md) r1=i,r2=0,md=a[i];
    else if(a[i]==md) r2=i;
    int rt=0;
    if(r2) rt=n+1;else rt=r1;
    for(int i=1;i<n;i++) if((x[i]==r1 && y[i]==r2) || (x[i]==r2 && y[i]==r1)) g[rt].push_back(x[i]),g[rt].push_back(y[i]);
    else g[x[i]].push_back(y[i]),g[y[i]].push_back(x[i]);
    dfs0(rt,0);
    for(int i=1;i<=n;i++) if(dep[i]>md) f[i]=n+2;
    else if(dep[i]<md) f[i]=n+1;
    else f[i]=siz[fr[i]];
    for(int i=1;i<=n;i++) id[i]=i;
    sort(id+1,id+n+1,[&](int x,int y){return f[x]==f[y]?x<y:f[x]<f[y];});
    for(int i=1;i<=n;i++) printf("%d ",id[i]);
    return 0;
}

K. Codenames

不会。

L. Neo-Robin Hood

考虑如果我们偷了 i 并且买通了 j,收益是 pimj,而交换之后收益是 pjmi。换句话说如果 pi+mi<pj+mj,偷 i 并买通 j 一定不如偷 j 并买通 i

那么将所有人按 pi+mi 排序,枚举位置 i,那么我们需要在 [1,i] 中选 x 个偷,在 [i+1,n] 中选 x 个买通。选取方案可以用堆维护。

注意到 x 是有单调性的,直接二分即可。复杂度 O(nlog2n)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<queue>
#define N 100010
using namespace std;
typedef long long ll;
struct node{
    int m,p;
}a[N];
int n;ll fl[N],fr[N];
priority_queue<int>q;
bool check(int x)
{
    while(!q.empty()) q.pop();ll s=0;
    for(int i=1;i<=n;i++)
    {
        q.push(-a[i].m),s+=a[i].m;
        if(q.size()>x) s-=-q.top(),q.pop();
        fl[i]=s;
    }
    while(!q.empty()) q.pop();s=0;
    for(int i=n;i;i--)
    {
        q.push(a[i].p),s+=a[i].p;
        if(q.size()>x) s-=q.top(),q.pop();
        fr[i]=s;
    }
    for(int i=x;i<=n-x;i++) if(fl[i]>=fr[i+1]) return true;
    return false;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i].m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i].p);
    sort(a+1,a+n+1,[&](node x,node y){return x.m+x.p>y.m+y.p;});
    int l=1,r=n,res=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) l=mid+1,res=mid;
        else r=mid-1;
    }
    printf("%d\n",res);
    return 0;
}
posted @   Flying2018  阅读(249)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示