VK Cup 2016 - Round 3

链接

A. Bear and Colors

水题,直接扫两遍就好了

B. Bear and Two Paths

构造题。考虑 m=n 是一个环凑一凑发现不合法。所以一定是 m=n+1

可以发现只要 n>4 一定可以放两个三元环,否则无解。

image

C. Levels and Regions

一通推式子之后有:

sj=ijtipj=ij1tispj=ijsitifj=mini<j{fi+p=i+1jspsitp}=mini<j{fi+p=i+1jsptpsip=i+1j1tp}=mini<j{fi+spjspisi(pjpi)}=mini<j{fispi+sipisipj}+spj

然后就是经典斜率优化。

复杂度 O(nk)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 200010
#define db double
using namespace std;
int t[N];
db s[N],p[N],sp[N],f[N],g[N],h[N];
int q[N],ql,qr;
db slope(int x,int y){return (h[y]-h[x])/(s[y]-s[x]);}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&t[i]),p[i]=1.0/t[i];
    for(int i=1;i<=n;i++) p[i]+=p[i-1],s[i]=s[i-1]+t[i],sp[i]=sp[i-1]+s[i]/t[i];
    for(int i=1;i<=n;i++) g[i]=sp[i];
    // for(int i=1;i<=n;i++) printf("%.6lf %.6lf %.6lf\n",p[i],s[i],sp[i]);
    for(int _=2;_<=k;_++)
    {
        ql=0,qr=0;q[ql]=0;
        for(int i=1;i<=n;i++)
        {
            while(ql<qr && slope(q[ql],q[ql+1])<p[i]) ql++;
            f[i]=h[q[ql]]-s[q[ql]]*p[i]+sp[i];
            h[i]=g[i]-sp[i]+s[i]*p[i];
            // printf("%d,%.6lf\n",q[ql],f[i]);
            while(ql<qr && slope(q[qr-1],q[qr])>=slope(q[qr],i)) qr--;
            q[++qr]=i;
        }
        // puts("");
        for(int i=1;i<=n;i++) g[i]=f[i],f[i]=0;
    }
    printf("%.10lf\n",g[n]);
    return 0;
}

D. Bearish Fanpages

赛时想了一个 O(nnlogn),结果被卡了。。。

后来发现自己是个傻子,考虑每个点的贡献挂到它的父亲上面,然后父亲挑选最小最大的点加上自己的权值后放入总的 set。

直接维护差值,复杂度 O(nlogn)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
#include<cassert>
#include<cmath>
#define N 100010
#define ll long long
using namespace std;
ll t[N],g[N];int f[N],deg[N],n,q;
struct node{
    int x;ll y;
    node(int X=0):x(X),y(g[x]){}
    node(int X,ll Y):x(X),y(Y){}
    bool operator <(const node a)const{return y==a.y?x<a.x:y<a.y;}
};
node operator +(const node a,ll v){return node(a.x,a.y+v);}
set<node>s,b[N];
set<int>h[N];
ll calc(int x){return t[x]-(deg[x]+1)*(t[x]/(deg[x]+2));}
ll delta_calc(int x,int y)
{
    ll v=calc(x);deg[x]+=y;
    v-=calc(x);deg[x]-=y;
    return v;
}
ll one(int x){return t[x]/(deg[x]+2);}
ll delta_one(int x,int y)
{
    ll v=one(x);deg[x]+=y;
    v-=one(x);deg[x]-=y;
    return v;
}
void bdel(int x){if(!b[x].empty()) s.erase(*b[x].begin()+one(x)),s.erase(*b[x].rbegin()+one(x));}
void badd(int x){if(!b[x].empty()) s.insert(*b[x].begin()+one(x)),s.insert(*b[x].rbegin()+one(x));}
void del(int x){bdel(f[x]),b[f[x]].erase(x),badd(f[x]);}
void add(int x){bdel(f[x]),b[f[x]].insert(x),badd(f[x]);}
void chg(int x,ll v){del(x),g[x]-=v,add(x);}
void chg_edge(int x,int v)
{
    chg(x,delta_calc(x,v));
    ll d=delta_one(x,v);
    chg(f[x],d);
    bdel(x),deg[x]+=v,badd(x);
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%lld",&t[i]);
    for(int i=1;i<=n;i++) scanf("%d",&f[i]),deg[f[i]]++,h[f[i]].insert(i);
    for(int i=1;i<=n;i++) g[i]+=calc(i),g[f[i]]+=one(i);
    for(int i=1;i<=n;i++) b[f[i]].insert(i);
    for(int i=1;i<=n;i++) badd(i);
    while(q --> 0)
    {
        int op,x,y;scanf("%d",&op);
        if(op==3) printf("%lld %lld\n",s.begin()->y,s.rbegin()->y);
        else if(op==2) scanf("%d",&x),printf("%lld\n",g[x]+one(f[x]));
        else
        {
            scanf("%d%d",&x,&y);
            del(x);
            chg(f[x],one(x)),chg(y,-one(x));
            h[f[x]].erase(x);
            chg_edge(f[x],-1);chg_edge(y,1);
            h[y].insert(x);
            f[x]=y;add(x);
        }
    }
    return 0;
}

E. Bear and Destroying Subtrees

看到期望题输出实数就要注意了。。。

fu,i:u 深度大于等于 i 的概率。

fu,i=1vson(112fv,i1)

设置一个阈值 M,有:

ans=i=1Mfu,i

注意到当 M>40 的时候贡献可以忽略。所以只要处理一个点向上 40 个点即可。

复杂度 O(nM)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 500010
#define M 51
#define db double
using namespace std;
int fa[N];db f[N][M];
int main()
{
    int n=1,q;
    scanf("%d",&q);
    while(q --> 0)
    {
        int op,x;scanf("%d%d",&op,&x);
        if(op==2)
        {
            db res=0;
            for(int j=1;j<M;j++) res+=f[x][j];
            printf("%.10lf\n",res);
        }
        else
        {
            ++n;fa[n]=x;f[n][0]=1;
            int u=x,p=n;db v=1;
            for(int d=1;d<M && u;d++,p=u,u=fa[u])
            {
                db w=f[u][d];
                f[u][d]=1-(1-w)/v*(1-0.5*f[p][d-1]);
                v=1-0.5*w;
            }
        }
    }
    return 0;
}

F. Bears and Juice

考虑信息论,总共 n 只熊,k 天,最多能睡 m 头熊,那么这里的信息量是 i=0m(ni)ki

实际上答案就是这个。

注意 232 不是质数,所以要手动约分分子分母。

复杂度 O(p3+pq)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 100010
#define ui unsigned int
using namespace std;
ui gcd(ui x,ui y){return y==0?x:gcd(y,x%y);}
void div(vector<ui>&a,vector<ui>&b){for(ui &i:a) for(ui &j:b){ui g=gcd(i,j);i/=g,j/=g;}}
ui f[N];
int main()
{
    int n,p,q;
    ui ans=0;
    scanf("%d%d%d",&n,&p,&q);p=min(p,n-1);
    for(int j=0;j<=p;j++)
    {
        vector<ui>a,b;
        for(int i=1;i<=j;i++) a.push_back(n-i+1),b.push_back(i);
        div(a,b);f[j]=1;
        for(int i=0;i<j;i++) f[j]*=a[i];
    }
    for(ui i=1;i<=q;i++)
    {
        ui res=0,pi=1;
        for(int j=0;j<=p;j++) res+=f[j]*pi,pi*=i;
        ans^=res*i;
    }
    cout<<ans;
    return 0;
}

G. Choosing Ads

经典问题。

考虑主元素定理:每次选择两个不同的数消掉,最后主元素一定被剩下。

那么这里就扩展一下:假设至少出现 1/p,那么每次选择 p 个不同的数消掉,最后至少出现 1/p 的数一定被留下。

合并时暴力将一边的元素扔到另一边即可。

复杂度 O(52nlogn)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 300010
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pi;
int p;
struct node{
    int c;
    pi v[5];
    pi& operator [](int x){return v[x];}
}t[N<<2];
int tag[N<<2];
node operator +(node a,pi x)
{
    // if(x.fi==0) throw;
    for(int i=0;i<a.c;i++) if(a[i].fi==x.fi){a[i].se+=x.se;return a;}
    if(a.c<p){a[a.c++]=x;return a;}
    int q=0;
    for(int i=1;i<a.c;i++) if(a[i].se<a[q].se) q=i;
    if(a[q].se<x.se) swap(a[q],x);
    for(int i=0;i<a.c;i++) a[i].se-=x.se;
    return a;
}
node operator +(node a,node b){for(int i=0;i<b.c;i++) a=a+b[i];return a;}
void setg(int u,int c,int v){t[u].c=1,tag[u]=v,t[u][0]=mp(v,c);}
void push(int u,int l,int r)
{
    if(!tag[u]) return;
    int mid=(l+r)>>1;
    setg(u<<1,mid-l+1,tag[u]);
    setg(u<<1|1,r-mid,tag[u]);
    tag[u]=0;
}
int a[N];
void build(int u,int l,int r)
{
    if(l==r){t[u][0]=mp(a[l],1);t[u].c=1;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 L,int R,int v)
{
    if(L<=l && r<=R){setg(u,r-l+1,v);return;}
    int mid=(l+r)>>1;push(u,l,r);
    if(L<=mid) insert(u<<1,l,mid,L,R,v);
    if(R>mid) insert(u<<1|1,mid+1,r,L,R,v);
    t[u]=t[u<<1]+t[u<<1|1];
}
node qry(int u,int l,int r,int L,int R)
{
    if(L<=l && r<=R) return t[u];
    int mid=(l+r)>>1;push(u,l,r);
    if(R<=mid) return qry(u<<1,l,mid,L,R);
    else if(L>mid) return qry(u<<1|1,mid+1,r,L,R);
    return qry(u<<1,l,mid,L,R)+qry(u<<1|1,mid+1,r,L,R);
}
int main()
{
    int n,m;
    scanf("%d%d%d",&n,&m,&p);p=100/p;
    // printf("%d\n",p);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int op,l,r,w;scanf("%d%d%d",&op,&l,&r);
        if(op==1) scanf("%d",&w),insert(1,1,n,l,r,w);
        else
        {
            node a=qry(1,1,n,l,r);
            printf("%d ",a.c);
            for(int j=0;j<a.c;j++) printf("%d ",a[j].fi);puts("");
        }
    }
    return 0;
}
posted @   Flying2018  阅读(64)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示