九省联考2018 简要题解

D1T1 一双木棋

t[i] t [ i ] 为第i列已经填了的格子数,则对于一个合法的状态,一定有 t[i+1]<=t[i] t [ i + 1 ] <= t [ i ]
于是合法的状态总数就是个组合数,不是很多可以全部搜出来丢进hash表里面
然后就可以直接dp啦

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void up(int &a,const int &b){ if(a<b)a=b; }
inline void down(int &a,const int &b){ if(a>b)a=b; }
const int maxn = 12;
const int maxk = 1<<22;
const int hashmod = 2333333;

int n,m;
int A[maxn][maxn],B[maxn][maxn];
vector<int>V[maxk],Vi[maxn*maxn];
int f[maxk],vis[maxk],tot;

struct Hash_Table
{
    int cnt;
    int h1[hashmod<<1],h2[hashmod<<1],hc[hashmod<<1];
    int nex[hashmod<<1];
    void init()
    {
        cnt=hashmod;
        memset(nex,-1,sizeof nex);
    }
    void get_hash(int t[],int &c1,int &c2)
    {
        c1=c2=0;
        for(int i=1;i<=m;i++) c1=(c1*19+t[i])%hashmod;
        for(int i=m;i>=1;i--) c2=c2*23+t[i];
    }
    void ins(int t[],int num)
    {
        int c1,c2; get_hash(t,c1,c2);
        if(!hc[c1]) { h2[c1]=c2; hc[c1]=num; return; }
        for(;nex[c1]!=-1;c1=nex[c1]);
        nex[c1]=++cnt;
        h2[cnt]=c2; hc[cnt]=num;
    }
    int find_(int t[])
    {
        int c1,c2; get_hash(t,c1,c2);
        for(int i=c1;i!=-1;i=nex[i])
            if(h2[i]==c2) return hc[i];
        return -1;
    }
}Hash;

int t[maxn];
void dfs(const int nowi)
{
    if(nowi>m)
    {
        ++tot; int sum=0;
        for(int i=1;i<=m;i++) V[tot].push_back(t[i]),sum+=t[i];
        //for(int i=1;i<=m;i++) printf("%d ",t[i]); printf("    %d\n",tot);
        Hash.ins(t,tot);
        Vi[sum].push_back(tot);
        return;
    }
    for(int j=t[nowi-1];j>=0;j--)
        t[nowi]=j,dfs(nowi+1);
}
void upd(int &x,const int &y,const int i) { (i&1)?up(x,y):down(x,y); }

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    Hash.init();

    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&A[i][j]);
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&B[i][j]);

    t[0]=n; t[m+1]=-1; dfs(1);
    f[Vi[n*m][0]]=0; vis[Vi[n*m][0]]=1;
    for(int i=n*m;i>0;i--)
    {
        for(int j=0;j<(int)Vi[i].size();j++)
        {
            int ii=Vi[i][j]; if(vis[ii])
            {
                for(int l=1;l<=m;l++) t[l]=V[ii][l-1];
                for(int l=1;l<=m;l++) if(t[l]&&t[l+1]<t[l])
                {
                    int temp=f[ii]+((i&1)?A[t[l]][l]:-B[t[l]][l]);
                    t[l]--;
                    int nexi=Hash.find_(t);
                    if(!vis[nexi]) vis[nexi]=1,f[nexi]=temp;
                    else upd(f[nexi],temp,i);
                    t[l]++;
                }
            }
        }
    }

    //for(int i=1;i<=tot;i++) printf("%d ",f[i]);
    printf("%d\n",f[Vi[0][0]]);

    return 0;
}

D1T2 IIIDX

把0号节点也建进去就是一棵树,给每个点分配一个点权,要求 cfa<=cson c f a <= c s o n ,编号按bfs序递增,求字典序最大的排列
(感觉我转化的题意有点问题?)

先将所有权值离散化
有个想法是给每个点的子树分配一个权值区间[L,R],给子树的根权值L,接着按编号从小到大遍历根的所有孩子,每次在[L+1,R]里取出后面的siz个值给这棵子树
这个做法在有相同权值的时候是错的(然而我一开始觉得他仍然是对的…….在LOJ上拿了60pts)

正解是这样的
我们将所有权值从大到小排序(相同权值的可以合并到一起),下面我们定义一个权值左边的数指的是比他大的权值,右边的数指的是比他小的权值
然后按照编号顺序(也是bfs序)从小到大给每个点分配权值
给一个点分配权值时,因为这个点的权值比他的子树里所有点都小,同时为了字典序最大,我们要在比他大的可分配权值数量>=siz-1的约束下找到最大的权值

考虑怎么动态维护这个东西,我们记一个 Fi F i 表示 i i 左边,已经确定被使用的数的数量,用线段树每个位置维护iFi(即左边可用数的数量)和区间里这个值的min,给一个点分配权值时,找到最右的值 i i ,满足j>=i,jFj>=siz(这个查找可以在线段树上二分实现),把这个值给i,同时我要在 i i 左边给i的子树预留 siz1 s i z − 1 个空位,就对于 j>=i,jFj=siz j >= i , j − F j − = s i z
分配到一个点 i i 时,要把他的父亲预留的siz-1个位置加回去
需要的操作只有线段树上二分和线段树的区间加减

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 510000;

int n; double K;
int a[maxn],To[maxn],num[maxn],sum[maxn],cnt;
inline bool cmp(const int x,const int y){return x>y;}

vector<int>V[maxn];
int fa[maxn],siz[maxn];
void dfs(const int x)
{
    siz[x]=1;
    for(int i=0;i<(int)V[x].size();i++)
    {
        int y=V[x][i];
        fa[y]=x; dfs(y);
        siz[x]+=siz[y];
    }
}

struct Segment
{
    int seg[maxn<<2],flag[maxn<<2];
    void pushdown(int x)
    {
        if(!flag[x]) return;
        int fl=flag[x]; flag[x]=0;
        seg[x<<1]+=fl,flag[x<<1]+=fl;
        seg[x<<1|1]+=fl,flag[x<<1|1]+=fl;
    }
    void pushup(int x){ seg[x]=min(seg[x<<1],seg[x<<1|1]); }
    void build(const int x,const int l,const int r)
    {
        if(l==r) { seg[x]=sum[l];return; }
        int mid=(l+r)>>1;
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
        pushup(x);
    }
    int lx,rx,c;
    void upd(const int x,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(lx<=l&&r<=rx) { seg[x]+=c;flag[x]+=c; return; }
        pushdown(x);
        int mid=(l+r)>>1;
        upd(x<<1,l,mid); upd(x<<1|1,mid+1,r);
        pushup(x);
    }
    int k;
    int Find(const int x,const int l,const int r)
    {
        if(l==r) return seg[x]>=k?l:l+1;
        pushdown(x);
        int mid=(l+r)>>1;
        if(seg[x<<1|1]>=k) return Find(x<<1,l,mid);
        else return Find(x<<1|1,mid+1,r);
    }
}seg;
int loc[maxn],vis[maxn];

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    read(n); scanf("%lf",&K);
    for(int i=1;i<=n;i++) read(a[i]); a[n+1]=1;
    sort(a+1,a+n+1+1,cmp);

    for(int i=1;i<=n+1;i++)
    {
        if(a[i]!=a[i-1]) To[++cnt]=a[i];
        num[cnt]++;
    }
    for(int i=1;i<=cnt;i++) sum[i]=sum[i-1]+num[i];
    seg.build(1,1,cnt);

    for(int i=1;i<=n;i++) V[(int)((double)i/K)].push_back(i);
    dfs(0);
    for(int i=0;i<=n;i++)
    {
        if(i&&!vis[fa[i]]) vis[fa[i]]=1,seg.lx=loc[fa[i]],seg.rx=cnt,seg.c=siz[fa[i]]-1,seg.upd(1,1,cnt);
        seg.k=siz[i]; loc[i]=seg.Find(1,1,cnt);
        seg.lx=loc[i],seg.rx=cnt,seg.c=-siz[i],seg.upd(1,1,cnt);
    }
    for(int i=1;i<n;i++) printf("%d ",To[loc[i]]);
    printf("%d\n",To[loc[n]]);

    return 0;
}

D1T3 秘密袭击

正解似乎是把状态表示成生成函数,然后用点值表达式操作,就可以用线段树合并快速合并背包,最后再用拉格朗日插值把系数插回来,戳这里
我还不太会,就skip掉了qaq,挖一个坑

这题暴力可以跑过去,而且暴力的做法似乎还挺多的
我们考虑从大到小枚举第k大是哪个点的值,树上>=这个值的点权值标为1,<的标为0,那么就是要计算一定包含这个点的和=k的联通块有多少个
然后这就是个树形依赖背包,可以在O(nk)的复杂度内完成
注意到我们需要统计的值只有n-k个,其他是不可能为联通块内第k大的,所以这题的暴力复杂度是 O((nk)nk) O ( ( n − k ) n k ) 的,因为正解常数比较大,时限也就放的比较大,于是暴力就能跑过去了…

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 1700;
const int mod  = 64123;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}

int n,K,W;
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}

struct node
{
    int x,i;
    friend inline bool operator <(const node a,const node b){return a.x<b.x;}
}s[maxn];
int ci[maxn],root;

int f[maxn][maxn],fa[maxn];
void dp(int x)
{
    int ff=fa[x];
    if(ci[x]) for(int j=0;j<K;j++) f[x][j+1]=f[ff][j];
    else for(int j=0;j<=K;j++) f[x][j]=f[ff][j];
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa[x])
    {
        fa[y]=x,dp(y);
        for(int j=0;j<=K;j++) add(f[x][j],f[y][j]);
    }
}

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    scanf("%d%d%d",&n,&K,&W);
    for(int i=1;i<=n;i++) scanf("%d",&s[i].x),s[i].i=i;
    sort(s+1,s+n+1);
    for(int i=1;i<n;i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        ins(x,y); ins(y,x);
    }

    int ans=0;
    for(int i=n;i>=1;i--)
    {
        ci[s[i].i]=1;
        if(i<=n-K+1)
        {
            for(int j=1;j<=n;j++) for(int k=0;k<=K;k++) f[j][k]=0;
            root=s[i].i;
            f[0][0]=1;
            fa[root]=0; dp(root);
            add(ans,(ll)s[i].x*f[root][K]%mod);
        }
    }
    printf("%d\n",ans);

    return 0;
}

D2T1 劈配

这个网络流我做的时候T惨了,然后膜了一下跑的特别快的选手的程序,发现我的写法非常的不优美,比如网络流在一个残余网络上判若干次增广路,可以倒着bfs一遍,判点i有没有增广路直接看i的dep就行了(根本不需要跑一遍dicnic哇)

这个匹配显然是可以用网络流做的
第一问因为要求一个字典序的最优,我们只能是去枚举每个人的每个优先级看有没有增广路,有的话就跑一遍网络流流完这个流量
第二问我们也是要枚举把这个人移到哪个优先级,然后找一下有没有增广路,这个东西满足二分性可以二分,但实际上第二问可以不二分,他是可以和第一问一起做的

具体来说,记第一问的答案为ans1[i],第二问的答案为ans2[i]
我们枚举到第i个人的时候,对于所有的j>=i,我们都去判ans1[j]选的这一档志愿是否仍然可行(是否还有增广路),没有就要延下一档,然后尝试用ans1[j]去更新ans2[j],最后对第i个人跑一次dicnic

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 405;
const int maxm = maxn*maxn*2;

int n,m;
int b[maxn],s[maxn];
int st,ed;
struct edge{int x,y,c,nex;}a[maxm]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c)
{
    a[++len]=(edge){x,y,c,fir[x]};fir[x]=len;
    a[++len]=(edge){y,x,0,fir[y]};fir[y]=len;
}
inline void ins2(const int x,const int y,const int c){a[++len]=(edge){x,y,c,fir[x]};fir[x]=len;}

vector<int>V[maxn][maxn];

int h[maxn];
queue<int>q;
struct Max_Flow
{
    bool bfs()
    {
        for(int i=1;i<=ed;i++) h[i]=0;
        h[st]=1; q.push(st);
        while(!q.empty())
        {
            const int x=q.front(); q.pop();
            for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(a[k].c&&!h[y])
                h[y]=h[x]+1,q.push(y);
        }
        return h[ed]>0;
    }
    int dfs(const int x,const int flow)
    {
        if(x==ed) return flow;
        int delta=0;
        for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
        {
            if(a[k].c&&h[y]==h[x]+1)
            {
                int minc=dfs(y,min(a[k].c,flow-delta));
                a[k].c-=minc,a[k^1].c+=minc;
                delta+=minc;
            }
            if(delta==flow) return delta;
        }
        if(!delta) h[x]=0;
        return delta;
    }
    int Flow()
    {
        int re=0;
        while(bfs()) re+=dfs(st,INT_MAX);
        return re;
    }
}Dicnic;
void bfs2()
{
    for(int i=1;i<=ed;i++) h[i]=0;
    h[ed]=1; q.push(ed);
    while(!q.empty())
    {
        const int x=q.front(); q.pop();
        for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(a[k^1].c&&!h[y])
            h[y]=h[x]+1,q.push(y);
    }
}

int ans1[maxn],ans2[maxn];
bool Check(int x,int u)
{
    while(ans1[x]<=u)
    {
        for(int t:V[x][ans1[x]])
            if(h[n+t]) return true;
        ans1[x]++;
    }
    return false;
}

int main()
{
    int tcase;scanf("%d%*d",&tcase);
    while(tcase--)
    {
        scanf("%d%d",&n,&m); st=n+m+1,ed=st+1;
        for(int i=0;i<=n;i++) for(int j=1;j<=m;j++) V[i][j].clear();

        for(int i=1;i<=m;i++) scanf("%d",&b[i]);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                int x; scanf("%d",&x);
                if(x) V[i][x].push_back(j);
            }
        }
        for(int i=1;i<=n;i++) scanf("%d",&s[i]);

        len=1; memset(fir,0,sizeof fir);
        for(int i=1;i<=m;i++) ins(n+i,ed,b[i]);

        for(int i=1;i<=n;i++) ans1[i]=1,ans2[i]=0;
        for(int i=1;i<=n;i++)
        {
            bfs2();
            for(int j=i;j<=n;j++) if(Check(j,s[j])) ans2[j]=i;
            if(Check(i,m))
            {
                ins(st,i,1);
                for(int t:V[i][ans1[i]])
                    ins(i,n+t,1);
                Dicnic.Flow();
            }
        }

        for(int i=1;i<n;i++) printf("%d ",ans1[i]); printf("%d\n",ans1[n]);
        for(int i=1;i<n;i++) printf("%d ",i-ans2[i]); printf("%d\n",n-ans2[n]);
    }

    return 0;
}

D2T2 林克卡特树

我们对这棵树砍K刀后会把他分成K+1部分,最后拼起来的答案就是这K+1棵树的直径和,难以发现问题就是成求K+1条点不相交的链的和的最大值
这个东西可以用一个dp做,f[i][0/1/2][j]表示i的子树内有j条链,i的度数是0/1/2,链权和的最大值

我们将 f[i][0/1/2][j] f [ i ] [ 0 / 1 / 2 ] [ j ] 记作一个关于 j j 的函数f(i,0/1/2),他是个凸函数,然后就是个和某题很像的套路了
我们想要求出 f[i][0/1/2][K+1] f [ i ] [ 0 / 1 / 2 ] [ K + 1 ] 的值,这个东西直接dp复杂度比较高,但我们是可以快速求出极值点 j j 的位置和值的,那么我们可以通过给这个函数加上kx调整极值点位置使得他落在我们想要的位置 K K 处,于是我们二分一个k,求出 f(i,l)+kj f ( i , l ) + k j 的极值点,根据这个东西调整 k k
我和别人后面那部分的做法似乎不太一样?好像只有我是二分到小数的(然后光荣垫底了)
复杂度O(nlogV)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define ld long double
#define inf 1e12
using namespace std;

inline void read(int &x)
{
    int f=0; char c; while(!((c=getchar())>='0'&&c<='9')) if(c=='-') f=1;
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
    if(f) x=-x;
}
const int maxn = 310000;
const ld  eps  = 1e-6;

int n,K;
struct edge{int y,c,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c){a[++len]=(edge){y,c,fir[x]};fir[x]=len;}

struct node
{
    int x; ld val;
    friend inline node operator +(const node x,const node y){ return (node){x.x+y.x,x.val+y.val}; }
}f[maxn][3],g[3],ans[maxn];
inline void upd(node &x,const node &y) { if(x.val+eps<y.val||(abs(x.val-y.val)<eps&&x.x<y.x)) x=y; }

ld ki;
void dp(int x,int fa)
{
    f[x][0]=(node){0,0};
    f[x][1]=(node){0,-inf};
    f[x][2]=(node){1,ki};
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa)
    {
        dp(y,x);
        for(int i=0;i<3;i++) g[i]=f[x][i];

        for(int i=0;i<3;i++) upd(f[x][i],ans[y]+g[i]);
        int cc=a[k].c;
        upd(f[x][1],(node){g[0].x+f[y][0].x+1,cc+g[0].val+f[y][0].val+ki});
        upd(f[x][1],(node){g[0].x+f[y][1].x,cc+g[0].val+f[y][1].val});
        upd(f[x][2],(node){g[1].x+f[y][0].x,cc+g[1].val+f[y][0].val});
        upd(f[x][2],(node){g[1].x+f[y][1].x-1,cc+g[1].val+f[y][1].val-ki});
    }
    ans[x]=f[x][0];
    for(int i=1;i<3;i++) upd(ans[x],f[x][i]);
}
int Solve() { dp(1,0); return ans[1].x; }

ld U;

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    read(n); read(K); K++; U=0.0;
    for(int i=1;i<n;i++)
    {
        int x,y,c; read(x); read(y); read(c);
        ins(x,y,c); ins(y,x,c);
        if(c>0) U+=c;
    }

    ld l=-U,r=U;
    while(r-l>eps)
    {
        ld mid=(l+r)/2.0; ki=mid;
        int loc=Solve();
        if(loc>=K) r=mid;
        else l=mid;
    }
    ld Ans=-r*K+ans[1].val+1e-3;
    printf("%.0Lf\n",Ans);

    return 0;
}

D2T3 制胡窜

这题感觉讨论有点多而且有点码农我就没写了(其实是因为不知道咋写),详细题解可以戳这里
大概就是考虑计算答案的补集,即i,j分割的三段内都没有这个串的情况数
那么就是求对于这个串出现的所有位置,串内至少都被切了一刀的方案数
S[l~r]出现的所有位置,我们可以建出parent树后,在S[1~r]那个点开始倍增跳到串长覆盖r-l+1的节点,于是我们可以在用线段树合并预处理每个点的right后,每个询问用log的时间找到他出现的所有位置
我们可以推出答案的柿子,然后在线段树合并的时候处理出要维护的这些东西

边界的讨论有点恶心,细节还是挺多的
(其实那个题解写的还是挺详细的)

### 回答1: 黄河九省地图数据Shp是一种包含九个省份的地理信息系统文件格式,可以在各种GIS软件中使用。这些九个省份包括青海、甘肃、宁夏、内蒙古、陕西、山西、河南、山东和河北。该地图数据Shp覆盖了全部黄河流域,以黄河为中心,包括了黄河流域内的山脉、河流、湖泊、城市、道路等地理要素。使用这个地图数据Shp可以提供各种黄河流域的地理信息分析与处理,如水文、水资源、水环境、地质灾害、城市规划、交通等领域。 该地图数据Shp由多个文件组成,其中包括.shp、.shx、.dbf等几个文件。其中.shp文件存储了地图数据几何图形信息,.shx文件存储了几何图形的索引信息,.dbf文件存储了属性数据信息。可以使用各种GIS软件打开这些文件,可以查看和编辑地图数据,进行地理信息分析和处理。 使用黄河九省地图数据Shp 可以进行各种有关黄河流域的地理信息分析和处理。例如,分析黄河流域内不同地区的地形、气候、土壤、地质等因素对河流水质、水量、水文周期以及水文气候等方面的影响。此外,还可以计算不同区域内的水资源量, 分析地区可能面临的水资源风险, 同时可以对水资源进行合理规划与管理。 总之,黄河九省地图数据Shp为黄河流域日常采集和分析水文资料提供了有力支持, 同时也可以支持城市建设和工程规划等方面的分析。因此,黄河九省地图数据Shp在黄河流域及周边地区的管理和规划等领域中具有广泛的应用前景。 ### 回答2: 黄河九省地图数据shp是一种包含有关中国黄河流域九个省份地理信息的数字地图文件。这些省份包括:青海、甘肃、宁夏、内蒙古、陕西、山西、河南、山东和河北。该地图文件将这些省份的边界和地理要素转换为几何图形,并将其存储在shp文件中,可实现对其进行空间分析和地图制作。 这个地图数据shp文件实用性很高,可以广泛应用于黄河流域相关的地理信息领域。例如,在自然资源管理方面,可以使用它来进行土地资源、水资源、气候资源和矿产资源的分析,以便有效管理和利用这些资源。而在交通运输、城市规划和环境保护方面,该文件可以用于路线规划、区域规划和环境监测,提高规划决策的准确性和精度。 此外,该地图数据shp文件可以应用在教育和科研领域,如地理信息系统、地理编码和遥感分析等方面,为学术研究和教学活动提供相关的数据支持和参考。总之,黄河九省地图数据shp文件是非常有价值的地理信息资源,对于研究和应用黄河流域相关的地理信息问题非常有帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值