XXII Open Cup , Grand Prix of Poland

链接

A. AMPPZ in the times of disease

题解

考虑如果确定了 k 个集合中的一个点,那么对于其他点,只需要分给距离最近的点即可。容易证明这是唯一合法的构造方案。

考虑如何确定 k 个集合中的点。容易发现,如果 k1,那么对于任何一个点,离他最远的点一定不在同一个集合中。同样对于任何一个 |S|<k 求出离集合 S 最近点最远的点,那么这个点也一定不在 S 中。

复杂度 O(nk)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=2000010;
struct node{
    int x,y;
    node(int x=0,int y=0):x(x),y(y){}
}p[N];
ll dis2(node a,node b){return 1ll*(a.x-b.x)*(a.x-b.x)+1ll*(a.y-b.y)*(a.y-b.y);}
int a[N];ll d[N];
int main()
{
    int T;scanf("%d",&T);
    while(T --> 0)
    {
        int n,k;scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y);
        a[1]=1,d[1]=0;
        for(int i=2;i<=n;i++) d[i]=dis2(p[i],p[1]);
        for(int i=2;i<=k;i++)
        {
            int u=0;
            for(int j=1;j<=n;j++) if(d[j]>d[u]) u=j;
            a[i]=u;
            for(int j=1;j<=n;j++) d[j]=min(d[j],dis2(p[j],p[u]));
        }
        for(int i=1;i<=n;i++)
        {
            ll dis=1e18;int u=0;
            for(int j=1;j<=k;j++) if(dis2(p[i],p[a[j]])<dis) dis=dis2(p[i],p[a[j]]),u=j;
            printf("%d ",u);
        }
        puts("");
        for(int i=1;i<=n;i++) d[i]=0;
    }
    return 0;
}

B. Babushka and her pierogi

咕了

题解

考虑答案的下界,首先交换次数不可能低于 n 减置换环个数,并且交换极差和不可能低于 12|aipi|。可以证明,存在构造可以达到这个下界。

构造不会。

E. Epidemic

题解

考虑点 (i,j) 表示第 i 个人在 j 时间的情况,(i,j)(i,j1) 连一条有向边,表示如果 (i,j1) 有可能感染了,那么 (i,j) 也有可能感染了。可以发现,由于应当假设所有不保证是阴性的人都是阳性,所以一个阳性检测除了隔离一个人之外不会导致其他影响。对于一次聚会,把所有 (x,t)(y,t) 之间都连边,表示如果其中一个有可能感染,那么其余也有可能感染。对于一次阴性检测,相当于所有能导致这个点阳性的点都不是阳性,所以它能到达的所有点都是阴性,同时如果一个点不能通过不一定是阴性的点到达某个 (x,0),那么这个点也一定是阴性。

直接处理复杂度是 O(n2) 的。考虑实际上只要对每次聚会的人复制点即可,同时注意到一次聚会得到的复制点是等价的,不妨直接用一个点来代替这个团。这样点数和边数都是 O(n) 的,用 set 维护非阴性且未被隔离的点集。复杂度 O(nlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
using namespace std;
const int N=2000010;
int deg[N],id[N],neg[N],tt;vector<int>g[N],h[N];
void add(int u,int v){g[u].push_back(v),h[v].push_back(u),++deg[u];}
set<int>res,pos[N];
void new_node(vector<int> tmp)
{
    int u=++tt;
    for(int v:tmp)
        if(!neg[id[v]]) add(u,id[v]);
        else res.insert(v);
    for(int v:tmp)
        pos[u].insert(v),pos[id[v]].erase(v),id[v]=u;
    if(!deg[u])
    {
        for(int v:tmp) res.erase(v);
        neg[u]=true;
    }
}
void make_neg(int u)
{
    if(neg[u]) return;
    neg[u]=true;
    for(auto v:pos[u]) if(res.count(v)) res.erase(v);
    for(int v:g[u]) make_neg(v);
    for(int v:h[u]) if(!neg[v] && !--deg[v]) make_neg(v);
}
int las,n;
int decode(){int x;scanf("%d",&x);return (x-1+las)%n+1;}
int main()
{
    int t;
    scanf("%d",&t);
    for(int _=1;_<=t;_++)
    {
        int m;scanf("%d%d",&n,&m);
        tt=n;
        for(int i=1;i<=n;i++) res.insert(i),id[i]=i,pos[i].insert(i);
        for(int i=1;i<=m;i++)
        {
            char op[3];scanf("%s",op);
            if(op[0]=='K')
            {
                int k;scanf("%d",&k);
                vector<int>tmp;
                for(int j=1,x;j<=k;j++) x=decode(),tmp.push_back(x);
                new_node(tmp);
            }
            else if(op[0]=='N')
            {
                int x=decode();
                make_neg(id[x]);
            }
            else if(op[0]=='P')
            {
                int x=decode();
                if(id[x]) pos[id[x]].erase(x),res.erase(x),id[x]=0;
            }
            else
            {
                int x=decode();
                if(res.empty()){puts("TAK"),las=0;continue;}
                auto y=res.lower_bound(x);
                if(y==res.end()) y=res.begin();
                printf("NIE %d\n",las=*y);
            }
        }
        las=0;
        for(int i=1;i<=tt;i++) id[i]=0,pos[i].clear(),g[i].clear(),h[i].clear(),neg[i]=false,deg[i]=0;
        res.clear();
        tt=0;
    }
    return 0;
}

G. Gebyte’s Grind

题解

考虑把每个位置当成一个函数,容易发现每个函数总是可以被表示成一个三元组 (a,b,c),若 xa 则为 0,否则若 xb 则为 c,否则为 c+(xb)

容易发现两个这样的折线复合之后也是这样的折线,用线段树维护折线函数,这样每次询问可以直接线段树上二分解决。

复杂度 O(qlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=2000010;
typedef long long ll;
const ll inf=1e12;
struct node{
    ll a,b,c;//if x<=a : 0 . elif x<=b : c . else c+(x-b)
    node(ll a=0,ll b=0,ll c=0):a(a),b(b),c(c){}
    ll calc(ll x){return x<=a?0:(x<=b?c:c+(x-b));}
};
node operator +(node x,node y)
{
    if(x.c<=y.a) return node(y.a-x.c+x.b,y.b-x.c+x.b,y.c);
    if(x.c<=y.b) return node(x.a,y.b-x.c+x.b,y.c);
    return node(x.a,x.b,x.c-y.b+y.c);
}
node t[N<<2],a[N];
void build(int u,int l,int r)
{
    if(l==r){t[u]=a[l];return;}
    int mid=(l+r)>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    t[u]=t[u<<1]+t[u<<1|1];
}
void insert(int u,int l,int r,int p,node v)
{
    if(l==r){t[u]=v;return;}
    int mid=(l+r)>>1;
    if(p<=mid) insert(u<<1,l,mid,p,v);
    else insert(u<<1|1,mid+1,r,p,v);
    t[u]=t[u<<1]+t[u<<1|1];
}
int qry(int u,int l,int r,int p,ll &x)
{
    if(l==r)
    {
        if(t[u].calc(x)==0) return l;
        else{x=t[u].calc(x);return l+1;}
    }
    int mid=(l+r)>>1;
    if(p==l)
    {
        if(t[u<<1].calc(x)==0) return qry(u<<1,l,mid,p,x);
        x=t[u<<1].calc(x);
        return qry(u<<1|1,mid+1,r,mid+1,x);
    }
    if(p>mid) return qry(u<<1|1,mid+1,r,p,x);
    int v=qry(u<<1,l,mid,p,x);
    if(v<=mid) return v;
    else return qry(u<<1|1,mid+1,r,mid+1,x);
}
node read()
{
    char op[3];ll x;scanf("%s%lld",op,&x);
    if(op[0]=='B') return node(x,x,0);
    if(op[0]=='K') return node(x-1,inf,x);
    return node(0,x,x);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T --> 0)
    {
        int n,m;ll X;scanf("%d%d%lld",&n,&m,&X);
        for(int i=1;i<=n;i++) a[i]=read();
        build(1,1,n);
        while(m --> 0)
        {
            char op[3];int p;scanf("%s%d",op,&p);
            if(op[0]=='Z') insert(1,1,n,p,read());
            else
            {
                ll x=X;
                int v=qry(1,1,n,p,x);
                if(v<=p) puts("-1");
                else printf("%d\n",v-1);
            }
        }
    }
    return 0;
}

I. Interesting Numbers

题解

这题做法几乎一致。考虑建 Trie 树,然后设 f(x,y) 表示当前递归到 x 的子树和 y 的子树。对于每个位置,如果当前限制位为 1,那么转移到 f(x0,y1)+f(x1,y0),如果限制位为 0 转移到 max(f(x0,y0),f(x1,y1)),再和 x,y 各自子树大小取 minx=y 时特判,x=0y=0 时特判掉。

复杂度 O(nloga)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=30010,D=20;
int ch[N*D][2],siz[N*D],tt=1;
void insert(int x)
{
    int u=1;
    for(int i=D-1;i>=0;siz[u]++,u=ch[u][x>>i&1],i--) if(!ch[u][x>>i&1]) ch[u][x>>i&1]=++tt;
    siz[u]++;
}
int qry(int a,int b,int x,int d=D-1)
{
    if(d==-1) return max(siz[a],siz[b]);
    if(!a) return siz[b];if(!b) return siz[a];
    if(a==b)
    {
        if(x>>d&1) return qry(ch[a][0],ch[a][1],x,d-1);
        else return max(qry(ch[a][0],ch[a][0],x,d-1),qry(ch[a][1],ch[a][1],x,d-1));
    }
    if(x>>d&1) return qry(ch[a][0],ch[b][1],x,d-1)+qry(ch[a][1],ch[b][0],x,d-1);
    else return max({qry(ch[a][0],ch[b][0],x,d-1),qry(ch[a][1],ch[b][1],x,d-1),siz[a],siz[b]});
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t --> 0)
    {
        int n,x;scanf("%d%d",&n,&x),++x;
        for(int i=1,x;i<=n;i++) scanf("%d",&x),insert(x);
        printf("%d\n",qry(1,1,x));
        for(int i=1;i<=tt;i++) ch[i][0]=ch[i][1]=siz[i]=0;
        tt=1;
    }
    return 0;
}

M. Median

posted @   Flying2018  阅读(94)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示