JOI 2016 Final

链接

B. Collecting Stamps 2

考虑如果加的是 J 显然会加在最前面。如果加的是 I 会加在最后面,如果是 O 效果是增加前面的 J 数量乘上后面的 I 数量,分类讨论一下就好了。复杂度 O(n)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100010;
char s[N];
ll f[3];
int main()
{
    int n;scanf("%d%s",&n,s+1);
    f[0]=f[1]=f[2]=0;
    for(int i=n;i;i--){if(s[i]=='I') f[0]++;else if(s[i]=='O') f[1]+=f[0];else f[2]+=f[1];}
    ll ans=f[1]+f[2],res=f[2];
    f[0]=f[1]=f[2]=0;f[0]++;
    for(int i=n;i;i--){if(s[i]=='I') f[0]++;else if(s[i]=='O') f[1]+=f[0];else f[2]+=f[1];}
    ans=max(ans,f[2]);
    int w0=0,w1=0;
    for(int i=1;i<=n;i++) if(s[i]=='J') w0++;
    for(int i=n;i;i--){if(s[i]=='J') w0--;else if(s[i]=='I') w1++;ans=max(ans,res+1ll*w0*w1);}
    printf("%lld\n",ans);
    return 0;
}

C. Train Fare

考虑一条边从 1 变成 2 相当于这条边被断掉了。实际上只要对每个点求出到 1 所有最短路径都被断掉的时刻即可。

可以认为一个点最短路变化的时候,这个点就被删掉了。直接 BFS 过程中转移每个点被删除的时间即可。

复杂度 O(n+m)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define N 200010
using namespace std;
int nxt[N<<1],to[N<<1],w[N<<1],head[N],cnt;
void add(int u,int v,int w0){nxt[++cnt]=head[u];w[cnt]=w0;to[cnt]=v;head[u]=cnt;}
int x[N],y[N],t[N],f[N],g[N],ans[N];
queue<int>q;
int main()
{
    int n,m,k;scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]),t[i]=m+1;
    for(int i=1,x;i<=k;i++) scanf("%d",&x),t[x]=i;
    for(int i=1;i<=m;i++) add(x[i],y[i],t[i]),add(y[i],x[i],t[i]);
    memset(f,-1,sizeof(f));
    q.push(1),f[1]=0,g[1]=k+1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(f[v]==-1) f[v]=f[u]+1,g[v]=min(g[u],w[i]),q.push(v);
            if(f[v]==f[u]+1) g[v]=max(g[v],min(g[u],w[i]));
        }
    }
    for(int i=1;i<=n;i++) ans[g[i]]++;
    for(int i=1;i<=k+1;i++) ans[i]+=ans[i-1];
    for(int i=1;i<=k;i++) printf("%d\n",ans[i]);
    return 0;
}

D. Territory

设一轮操作结束后位置移动了 (x,y),不妨假设 x>0,y0(如果不是可以调整,若 x=0,y=0 特判即可)。考虑将所有可以通过 (+dx,+dy) 后互相到达的点归到一起。容易发现这些点按照 d 构成若干个区间,而区间总数是 O(n) 的。

枚举所有可能合法的等价类,相当于给定 4 组区间,问交的大小。直接四个指针扫一遍即可。

复杂度 O(nlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define N 200010
using namespace std;
int nxt[N<<1],to[N<<1],w[N<<1],head[N],cnt;
void add(int u,int v,int w0){nxt[++cnt]=head[u];w[cnt]=w0;to[cnt]=v;head[u]=cnt;}
int x[N],y[N],t[N],f[N],g[N],ans[N];
queue<int>q;
int main()
{
    int n,m,k;scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]),t[i]=m+1;
    for(int i=1,x;i<=k;i++) scanf("%d",&x),t[x]=i;
    for(int i=1;i<=m;i++) add(x[i],y[i],t[i]),add(y[i],x[i],t[i]);
    memset(f,-1,sizeof(f));
    q.push(1),f[1]=0,g[1]=k+1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(f[v]==-1) f[v]=f[u]+1,g[v]=min(g[u],w[i]),q.push(v);
            if(f[v]==f[u]+1) g[v]=max(g[v],min(g[u],w[i]));
        }
    }
    for(int i=1;i<=n;i++) ans[g[i]]++;
    for(int i=1;i<=k+1;i++) ans[i]+=ans[i-1];
    for(int i=1;i<=k;i++) printf("%d\n",ans[i]);
    return 0;
}

E. Geologic Fault

考虑倒过来处理,即维护最后在地表的点初始的深度。

将坐标旋转 45,容易发现每次操作是将某个前缀向下移,或者某个后缀向上右移。容易发现这些点仍然构成偏序关系,用一个线段树即可。

复杂度 O(nlogn)

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