HGOI7.28集训题解

题解

省选模拟赛第三天orz。慢慢有点适应了呢…感觉省选就是打一把暴力…但暴力其实感觉还行。


第一题——小象和老鼠(lemouse)

题目描述

S国的动物园是一个N*M的网格图,左上角的坐标是(1,1),右下角的坐标是(N,M)。

小象在动物园的左上角,它想回到右下角的家里去睡觉,但是动物园中有一些老鼠,而小象又很害怕老鼠。

动物园里的老鼠是彼此互不相同的。小象的害怕值定义为他回家的路径上可以看见的不同的老鼠的数量。

若小象当前的位置为(x1,y1),小象可以看见老鼠,当且仅当老鼠的位置(x2,y2)满足|x1-x2|+|y1-y2|<=1。

由于小象很困了,所以小象只会走一条最近的路回家,即小象只会向下或者向右走。

现在你需要帮小象确定一条回家的路线,使得小象的害怕值最小。

输入格式

第一行包含两个用空格隔开的整数,N和M。接下来一个N*M的矩阵表示动物园的地图。

其中Aij表示第i行第j列上老鼠的数量。若Aij=0则表示当前位置上没有老鼠(小象的家里也可能存在老鼠)。

输出格式
输出一个整数,表示路线最小的害怕值是多少。


数据范围与约定

对于10%的数据,1<=N,M<=5。

对于100%的数据1<=N,M<=1000,0<=Aij<=100。

  • emmmm感觉有点像过河卒?但状态转移的时候会有一个问题。那我们一层层的剥下来。
  • 首先我们知道,当前状态是由上面的状态和左边的状态转移过来的。而当前所在的格子的老鼠已经被上一个和左边的那个给包括进去了,则只需要将右边的和下面的加进来就可以了。
  • 但在打的时候你会发现一个问题。由于象走的时候将道路两侧的看见的老鼠都囊括进来
  • 但上一个状态也有两种来源若上一个状态也是从上上走下来的,则当前左边的老鼠没有算进,而当上面是从左边过来的,则当前左边的不能再算进去。
  • 从左边转移也是同理。
  • 那就需要在 f [ i ] [ j ] 后加上一维来表示上一个状态的来源。
  • 方程:(0左1上)

i f ( i 1 ) f [ i ] [ j ] [ 1 ] = m i n ( f [ i ] [ j ] [ 1 ] , m i n ( f [ i 1 ] [ j ] [ 0 ] , f [ i 1 ] [ j ] [ 1 ] + m p [ i ] [ j 1 ] ) ) ;

i f ( j 1 ) f [ i ] [ j ] [ 0 ] = m i n ( f [ i ] [ j ] [ 0 ] , m i n ( f [ i ] [ j 1 ] [ 0 ] + m p [ i 1 ] [ j ] , f [ i ] [ j 1 ] [ 1 ] ) ) ;

f [ i ] [ j ] [ 0 ] = f [ i ] [ j ] [ 0 ] + m p [ i ] [ j + 1 ] + m p [ i + 1 ] [ j ] ;
f [ i ] [ j ] [ 1 ] = f [ i ] [ j ] [ 1 ] + m p [ i ] [ j + 1 ] + m p [ i + 1 ] [ j ] ;


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#define LL long long
#define INF 0x3f3f3f
using namespace std;
void fff(){
    freopen("lemouse.in","r",stdin);
    freopen("lemouse.out","w",stdout);
}
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
const int MAXN=1005;
int f[MAXN][MAXN][2];//0 zuo 1 shang
int mp[MAXN][MAXN];
int n,m;
int main(){
//  fff();
    n=read();
    m=read();
    memset(mp,0,sizeof(mp));
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++){
            mp[i][j]=read();
        }
    }
    memset(f,INF,sizeof(f));
    f[1][1][0]=f[1][1][1]=mp[1][1]+mp[1][2]+mp[2][1];
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++){
            if(i==1&&j==1) continue;
            if(i-1){
                f[i][j][1]=min(f[i][j][1],min(f[i-1][j][0],f[i-1][j][1]+mp[i][j-1]));
            }
            if(j-1){
                f[i][j][0]=min(f[i][j][0],min(f[i][j-1][0]+mp[i-1][j],f[i][j-1][1]));
            }
            f[i][j][0]=f[i][j][0]+mp[i][j+1]+mp[i+1][j];
            f[i][j][1]=f[i][j][1]+mp[i][j+1]+mp[i+1][j];
        }
    }
    cout<<min(f[n][m][0],f[n][m][1]);
}

第二题——网络服务(serves)

题目描述

S国的网络系统由N个城市服务点和M条双向传输光缆构成。每个城市有一个评级Ri。每条光缆有一个传输时间Ti。

我们规定d(i,j)为城市i到城市j的最短传输时间(我们认为d(i,i)=0)。

现在城市之间有一种单向合作意愿。

我们说城市B愿意与城市A建立合作关系,当且仅当对于所有满足d(A,C)<=d(A,B)的城市C,都有R(C)<=R(B)。

一个城市的受欢迎程度Bi定义为愿意与其建立合作关系的城市数量。

现在S国政府想知道所有城市的受欢迎程度之和Sum是多少。

由于S国的网络系统规模有限

可以向你保证每个城市连接的光缆数目不超过10条,所有城市的受欢迎程度之和不超过30N。

输入格式

第一行包含两个用空格隔开的整数,N和M。接下来N行表示每个城市的评级Ri。

接下来M行,每行三个整数,Xi、Yi、Ti表示城市Xi和城市Yi之间有一条双向传输光缆,传输时间为Ti。

输出格式

输出一个整数,表示所有城市的受欢迎程度之和Sum。

数据范围与约定

对于10%的数据,满足N<=100。对于40%的数据,满足N<=1000。

对于100%的数据,满足N<=30000,1<=M<=5N,1<=Ri<=10,1<=Ti<=1000,Sum<=30N

  • 这个题目我刚开始没读太懂..打了把暴力骗了十分。
  • 题目的判断条件比较难懂,解释下就是如果城市B要和A建立合作就要满足所有R(C)>R(B)的城市都有d(A,C)>d(A,B)。
  • 看了下数据,如果每个店都做一遍spfa或者暴力floyd是绝对爆炸的。那么可以从R入手(反正 R 10 )。枚举所有的R(降序),对所有的扫描到的点进行堆优化dijkstra。
  • 设lim表示R比i点大的所有点当中到i的最短距离。(就是那个达到可以建立的前提的距离)。
  • 设dist[i]是到i的最短路,则当需要更新时,当且仅当lim[i]>dist[i]。
  • 那么就可以得到上方所需要的条件。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define INF 0x3f3f3f3f
#include <vector>
#include <queue>
using namespace std;
void fff(){
    freopen("serves.in","r",stdin);
    freopen("serves.out","w",stdout);
}
inline int read(){
    int x=0;
    bool flag=false;
    char ch=getchar();
    while ((ch!='-')&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') flag=true,ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(flag) x=-x;
    return x;
}
const int N=30010,M=300100;
int Next[M],to[M],len[M],cnt,head[N];

void add(int x,int y,int z){
    Next[++cnt]=head[x];
    to[cnt]=y;
    len[cnt]=z;
    head[x]=cnt;
}
int n,m;
int r[N];
int dist[N],lim[N],minr[N];
struct node{
    int x,w;
    bool operator < (const node temp) const{
        if(temp.w==w) return r[x]>r[temp.x];
        return temp.w<w;
    }
};
priority_queue <node> H;
int stack[M],tp=0,ans;
int nowrank;
void dijk(){
    while (!H.empty()){
        node sta=H.top();
        H.pop();
        if(sta.w!=dist[sta.x]) continue;
        stack[++tp]=sta.x;
        for (int i=head[sta.x];i!=-1;i=Next[i]){
            if(r[to[i]]>nowrank) continue;
            if(dist[to[i]]>sta.w+len[i]&&lim[to[i]]>sta.w+len[i]){
                dist[to[i]]=sta.w+len[i];
                H.push((node){to[i],dist[to[i]]});
            }
        }
    }
}
int main(){
//  fff();
    n=read(),m=read();
    memset(dist,0x3f,sizeof(dist));
    memset(lim,0x3f,sizeof(lim));
    memset(head,-1,sizeof(head));
    for (int i=1;i<=n;i++) r[i]=read();
    for (int i=1;i<=m;i++){
        int x,y,w;
        x=read(),y=read(),w=read();
        add(x,y,w);
        add(y,x,w);
    }
    for (int i=10;i>=1;i--){
        nowrank=i;
        memset(minr,0x3f,sizeof(minr));
        for (int j=1;j<=n;j++){
            if(r[j]==i){
                H.push((node){j,0});
                dist[j]=0;
                dijk();
            }
            while (tp){
                int k=stack[tp];
                tp--;
                if(lim[k]>dist[k]) ans++;
                minr[k]=min(minr[k],dist[k]);
                dist[k]=INF;
            }
        }
        for (int j=1;j<=n;j++){
            lim[j]=min(minr[j],lim[j]);
        }
    }
    cout<<ans;

}

第三题——秘密武器(weapon)

题目描述

S国新研制了一种秘密武器,由排成一列的N个发射器构成,每个发射器有一个power值Pi,武器系统有一个阀值F。

武器发动攻击时,首先需要规定一对正整数参数(a,len)(a+len*2+F-1<=N)

表示位于区间[a,a+len-1]和区间[a+len+F,a+len*2+F-1]内的发射器同时工作。

然而,当前后两段发射器的power值完全相同

即P[a+i]=P[a+len+F+i](0<=i<len)时,发射器会发生共振损坏武器系统。

S国当然不希望自己的秘密武器报废,于是他们想知道有多少对参数(a,len)会导致武器损坏。


输入格式

第一行两个正整数,N和F。

第二行N个正整数,表示发射器的power值Pi。

输出格式

一个整数,表示会损坏武器系统的参数对数。

  • 暴力全字匹配=30分,类KMP优化=60分。
  • hash+离散+段匹配=100

  • 反正不管怎么样都是要枚举len进行匹配的。那么就先枚举len。
  • 但你会发现和KMP一样,没有必要一位位去匹配。那就想到了hash。
  • 设frontl表示两段的最长公共前缀,backl是最长公共后缀,求一遍lcp。然后在考虑对答案的贡献。
  • 如果frontl=len,则表示这一段都是匹配的,则对答案的贡献就是len,否则就是当前的头nowl+frontl-i(i枚举len)+1。而nowl也相应的移动。
  • backl同理。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef  unsigned long long ull;
void fff(){
    freopen("weapon.in","r",stdin);
    freopen("weapon.out","w",stdout);
}
inline int read(){
    int x=0;
    bool flag=false;
    char ch=getchar();
    while ((ch!='-')&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') flag=true,ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(flag) x=-x;
    return x;
}
const int MAXN=100010;
int n,F,cnt=0;
int p[MAXN],X[MAXN],b[MAXN],k=0;
ull Hash[MAXN],f[MAXN]={1};
const ull mul=133331;
int bin(int x){
    int l=1,r=k;
    while (l<=r){
        int mid=(l+r)>>1;
        if(b[mid]<=x) l=mid+1;
        else r=mid-1;
    }
    return r;
}
ull getHash(const int &l,const int &r){
    return Hash[r]-Hash[l-1]*f[r-l+1];
}
int lcp(int x,int y){
    int l=0,r=min(n-x+1,n-y+1);
    while (l<=r){
        int mid=(l+r)>>1;
        if(getHash(x,x+mid-1)==getHash(y,y+mid-1)) l=mid+1;
        else r=mid-1;
    }
    return r;
}

int anti_lcp(int x,int y){
    int l=0,r=min(x,y);
    while (l<=r){
        int mid=(l+r)>>1;
        if(getHash(x-mid+1,x)==getHash(y-mid+1,y)) l=mid+1;
        else r=mid-1;
    }
    return r;
}
int ans=0;
int main(){
    n=read();F=read();
    for (int i=1;i<=n;i++)  p[i]=read(),X[i]=p[i];
    sort(X+1,X+n+1);
    X[0]=~0U>>1;
    for (int i=1;i<=n;i++) if(X[i]!=X[i-1]) b[++k]=X[i];
    for (int i=1;i<=n;i++) p[i]=bin(p[i]);
    for (int i=1;i<=n;i++) f[i]=f[i-1]*mul;
    for (int i=1;i<=n;i++) Hash[i]=Hash[i-1]*mul+p[i];
    for (int i=1;2*i+F<=n;i++){
        int nowl=0;
        for (int j=1;;){
            int len=min(i,n-(j+i+F-1));
            if(!len) break;
            int frontl=lcp(j,j+i+F);
            int backl=anti_lcp(j+len-1,j+F+i+len-1);
            frontl=min(frontl,len);
            backl=min(backl,len);
            if(frontl==len){
                if(nowl>=i) ans+=len;
                else ans+=max(0,nowl+frontl-i+1);
                nowl+=len;
            }else{
                if(nowl>=i) ans+=frontl;
                else ans+=max(0,nowl+frontl-i+1);
                nowl=backl;
            }
            j+=len;
        }
    }
    cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/81264211