hgoi#20191114

T1-宇宙魔方

有一个 \(n^3\) 的立方体,每次会给这个立方体的一层都加上 \(X\) 或整体转动这个立方体
现在给你若干次操作后的立方体,有一个格子上的值不知道,请你求出

解法

直接模拟回溯所有的操作
具体是每次找到一层的最小值
全部减去即可

ac代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
inline void read(int&x)
{
    x=0;int fh=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
    for(;isdigit(c);c=getchar())x=x*10+c-'0';
    x*=fh;
}
inline void print(int x)
{
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
int n,minn,a[110][110][110];
int main()
{
    freopen("cube.in","r",stdin);
    freopen("cube.out","w",stdout);
    read(n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                read(a[i][j][k]);
    for(int i=1;i<=n;i++)
    {
        minn=inf;
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                if(a[i][j][k]>=0)
                    minn=min(minn,a[i][j][k]);
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                a[i][j][k]-=minn;
        minn=inf;
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                if(a[j][i][k]>=0)
                    minn=min(minn,a[j][i][k]);
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                a[j][i][k]-=minn;
        minn=inf;
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                if(a[j][k][i]>=0)
                    minn=min(minn,a[j][k][i]);
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                a[j][k][i]-=minn;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                if(a[i][j][k]<0)
                    print(-a[i][j][k]-1);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

T2-战争

给你一个带权无向图,要求你每次走的边权都要严格递增
求最远能走的距离

解法

这个根本不用建图
\(f_{i,j}\) 表示 \(i\) 号点作为终点,上一个走的权值为 \(j\) 的最远距离
我们发现一条边只会更新两个点,所以把第二维压掉
具体见代码

ac代码

#include<bits/stdc++.h>
using namespace std;
inline void read(int&x)
{
    x=0;int fh=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
    for(;isdigit(c);c=getchar())x=x*10+c-'0';
    x*=fh;
}
inline void print(int x)
{
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
struct node
{
    int x,y,s;
    void init(){read(x),read(y),read(s);}
    bool operator<(const node&a)const{return s<a.s;}
}e[50010];
queue<int>q;
int n,m,ans,f[50010],g[50010];
int main()
{
    freopen("war.in","r",stdin);
    freopen("war.out","w",stdout);
    read(n),read(m);
    for(int i=1;i<=m;i++)e[i].init();
    sort(e+1,e+m+1);
    int nw=1,ed=1;
    while(nw<=m)
    {
        while(ed<m&&e[ed].s==e[ed+1].s)ed++;
        while(nw<=ed)
        {
            g[e[nw].x]=max(g[e[nw].x],max(f[e[nw].x],f[e[nw].y]+1));
            g[e[nw].y]=max(g[e[nw].y],max(f[e[nw].y],f[e[nw].x]+1));
            q.push(e[nw].x),q.push(e[nw].y),nw++;
        }ed++;
        while(!q.empty())f[q.front()]=g[q.front()],q.pop();
    }
    for(int i=0;i<n;i++)ans=max(ans,f[i]);print(ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

T3-和平

给你一棵带边权的树,若干次询问
每次询问 \(A、B、c\) 表示从A出发,走到B,只能通过边权大于等于 \(c\) 的点
求最多能走到哪里

解法

先拆成两部分,先从 \(A\) 走到 \(LCA\) ,再从 \(LCA\) 走到 \(B\)
如果停下的地方在第一部分,用倍增非常好做
但是如果停下的地方在第二部分怎么办呢
我们可以二分求这条路径的断点,然后求出一段的最小值,看是否符合要求
具体实现见代码

ac代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define mid (l+r>>1)
#define pb push_back
#define N 100010
#define K 18
using namespace std;
inline void read(int&x)
{
    x=0;int fh=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
    for(;isdigit(c);c=getchar())x=x*10+c-'0';
    x*=fh;
}
inline void print(int x)
{
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
vector<int>e[N],w[N];
int n,m,x,y,z,d[N],f[N][K],v[N][K];
void dfs(int u,int fa)
{
    int sz=e[u].size();
    for(int i=0;i<sz;++i)if(e[u][i]!=fa)
        f[e[u][i]][0]=u,v[e[u][i]][0]=w[u][i],
        d[e[u][i]]=d[u]+1,dfs(e[u][i],u);
}
inline int lca(int x,int y)
{
    if(d[x]<d[y])swap(x,y);
    if(d[x]>d[y])
    {
        int g=d[x]-d[y];
        for(int i=0;i<K;++i)
            if(g&(1<<i))
                x=f[x][i];
    }
    if(x==y)return x;
    for(int i=K-1;i>=0;--i)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
inline int get(int u,int g)
{
    int ans=inf;
    for(int i=0;i<K;++i)
        if(g&(1<<i))
            ans=min(ans,v[u][i]),
            u=f[u][i];
    return ans;
}
//得到u向上g条边的最小值
inline int find(int u,int g)
{
    for(int i=0;i<K;++i)
        if(g&(1<<i))
            u=f[u][i];
    return u;
}
//寻找u向上g条边的点
inline int query(int st,int ed,int G)
{
    int LCA=lca(st,ed),da=d[st]-d[LCA],db=d[ed]-d[LCA];
    int l=0,r=da+db,ans,D=get(st,da);
    while(l<=r)
        if((mid<=da?get(st,mid):min(D,get(find(ed,da+db-mid),mid-da)))<G)r=mid-1;else ans=mid,l=mid+1;
    //二分这个断点,如果在第一部分,那么路径只有一段
    //如果在第二部分,分成整个第一部分和第二部分断点上方的位置求解
    if(ans>da)return find(ed,da+db-ans);else return find(st,ans);
}
int main()
{
    freopen("peace.in","r",stdin);
    freopen("peace.out","w",stdout);
    read(n),read(m);
    for(int i=1;i<n;i++)
        read(x),read(y),read(z),
        e[x].pb(y),w[x].pb(z),
        e[y].pb(x),w[y].pb(z);
    dfs(1,0);
    for(int j=1;j<K;++j)
        for(int i=1;i<=n;++i)
            f[i][j]=f[f[i][j-1]][j-1],
            v[i][j]=min(v[i][j-1],v[f[i][j-1]][j-1]);
    for(int i=1;i<=m;++i)
        read(x),read(y),read(z),
        print(query(x,y,z)),putchar('\n');
    fclose(stdin);
    fclose(stdout);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/muronglin/p/hgoi-20191114.html