喵哈哈村的魔法考试 Round #15 (Div.2) 题解

哗啦啦村的奇迹果实(一)

题解:显然答案就是最大值减去最小值。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+7;

int n,a[maxn];

int main(){
    while(cin>>n){
        int x;scanf("%d",&x);
        int Mx = x;
        int Mi = x;
        for(int i=1;i<n;i++){
            scanf("%d",&x);
            Mx = max(Mx,x);
            Mi = min(Mi,x);
        }
        cout<<Mx-Mi<<endl;
    }
}

哗啦啦村的奇迹果实(二)

题解:虽然0可以与其他位置交换,但是0不改变其他数字的相对位置,所以只要找到相对位置,然后进行匹配即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 200005;
int a[maxn],b[maxn],tot1,tot2,n;
int solve(){
    tot1=0;
    tot2=0;
    for(int i=0;i<n;i++)
    {
        int x;scanf("%d",&x);
        if(x==0)continue;
        a[tot1++]=x;
    }
    for(int i=0;i<n;i++)
    {
        int x;scanf("%d",&x);
        if(x==0)continue;
        b[tot2++]=x;
    }
    int s=0;
    for(int i=0;i<tot2;i++)
    {
        if(b[i]==a[0])
        {
            s=i;
            break;
        }
    }
    if(tot1!=tot2){
        return puts("NO");
    }
    for(int i=0;i<tot1;i++)
        if(a[i]!=b[(i+s)%(tot2)])
            return puts("NO");
    return puts("YES");
}
int main()
{
    while(cin>>n)solve();
}

哗啦啦村的奇迹果实(三)

题解:动态规划。
f[i][j][0]表示当前小象位于格子(i,j)且上一个位置是(i-1,j)所看见的老鼠的最少数量。
f[i][j][1]表示当前小象位于格子(i,j)且上一个位置是(i,j-1)所看见的老鼠的最少数量。
f[i][j][0]=min(f[i-1][j][0]+a[i][j-1],f[i-1][j][1])+a[i+1][j]+a[i][j+1]
f[i][j][1]=min(f[i][j-1][0],f[i][j-1][1]+a[i-1][j])+a[i+1][j]+a[i][j+1]
answer=min(f[n][m][0],f[n][m][1])。
复杂度为 O(NM)。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1100
using namespace std;
int n,m;
int f[N][N];
int g[N][N];
int a[N][N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    memset(f,0x3f,sizeof(f));
    memset(g,0x3f,sizeof(g));
    f[1][1]=g[1][1]=a[1][1]+a[1][2]+a[2][1];
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(j-1>0)
                f[i][j]=min(f[i][j],min(f[i][j-1]+a[i-1][j]+a[i][j+1]+a[i+1][j],g[i][j-1]+a[i][j+1]+a[i+1][j]));
            if(i-1>0)
                g[i][j]=min(g[i][j],min(f[i-1][j]+a[i][j+1]+a[i+1][j],g[i-1][j]+a[i][j-1]+a[i][j+1]+a[i+1][j]));
        }
    }
    printf("%d\n",min(f[n][m],g[n][m]));
}

哗啦啦村的奇迹果实(四)

算法:最短路。
将“如果城市 B 愿意与城市 A 建立合作关系,当且仅当对于所有满足 d(A,C)<=d(A,B)的城市
C,都有 R(C)<=R(B)。”这一条件转化为“如果城市 B 愿意与城市 A 建立合作关系,当且仅当
对于所有满足 R(C)>R(B)的城市 C,都有 d(A,C)>d(A,B)。”
我们倒序枚举 R 的值 r,然后枚举 R(X)=r 的点 X,以每个点为起点对原图做最短路。设 lim[i]
表示所有点 K(R(K)>R(X))中到点 i 的最小距离,设 dist[i]表示 X 到 i 的最短路。对于点 i,在最
短路过程中,如果 dist[i]>=lim[i],那么表示比 X 的 R 值大的某个点到点 i 的最短距离要比 X
到 i 的距离短,所以 i 不会与 X 建立合作关系,而且不会用 dist[i]去更新其它点的最短路了。
因此,一个点用来更新其它点的条件是 dist[i]<lim[i],此时答案+1,因为答案<=30N,所以总
的更新次数不会超过 30N 次。所以最后复杂度为 O(kNlogN),k 为常数。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<ctime>

using namespace std;

const int INF=0x3f3f3f3f;
const int N=30010,M=300100;
int n,m,nowRank;
int head[N],key[M],Next[M],len[M],cnt;
int Rank[N],dis[N],lim[N],minr[N];
int stack[N],tp;
int top,ans;

struct Heap
{
    int x,w;
    Heap(void){}
    Heap(int _x,int _w):x(_x),w(_w){}
}heap[10*M];

void add(int x,int y,int z)
{
    Next[++cnt]=head[x];
    key[cnt]=y;
    len[cnt]=z;
    head[x]=cnt;
}

inline bool cmp(const Heap &a,const Heap &b)
{
    if(a.w!=b.w) return a.w>b.w;
    else return Rank[a.x]<Rank[b.x];
}

void dijk()
{
    while(top)
    {
        Heap sta=heap[1];
        pop_heap(heap+1,heap+top+1,cmp);
        top--;
        if(sta.w!=dis[sta.x]) continue;
        stack[++tp]=sta.x;
        for(int i=head[sta.x];~i;i=Next[i])
        {
            if(Rank[key[i]]>nowRank) continue;
            if(dis[key[i]]>sta.w+len[i]&&lim[key[i]]>sta.w+len[i])
            {
                dis[key[i]]=sta.w+len[i];
                heap[++top]=Heap(key[i],dis[key[i]]);
                push_heap(heap+1,heap+top+1,cmp);
            }
        }
    }
}

int main()
{
    memset(head,-1,sizeof head);
    memset(dis,0x3f,sizeof dis);
    memset(lim,0x3f,sizeof lim);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&Rank[i]);
    for(int i=1,x,y,z;i<=m;i++)
        scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
    for(int i=10;i>=1;i--)
    {
        nowRank=i;
        memset(minr,0x3f,sizeof minr);
        for(int j=1;j<=n;j++)
            if(Rank[j]==i)
            {
                heap[++top]=Heap(j,0);
                dis[j]=0;
                dijk();
                for(;tp;tp--)
                {
                    int k=stack[tp];
                    if(lim[k]>dis[k]) ans++;
                    minr[k]=min(minr[k],dis[k]);
                    dis[k]=INF;
                }
            }
        for(int j=1;j<=n;j++)
            lim[j]=min(minr[j],lim[j]);
    }
    cout<<ans<<endl;
    return 0;
}

哗啦啦村的奇迹果实(五)

算法:字符串 hash
枚举两段的长度 len 和第一段的起点 i,我们定义 L 为第一段与第二段的最长公共后缀,当
L>=len 的时候答案+1,而起点为 i+1 时 L 的大小仅仅取决于起点为 i 时 L 大小和 a[i+len]与
a[i+2len+F]的相等关系:
L[i+1] = L[i] + 1 (a[i+len]=a[i+2
len+F])
L[i+1] = 0 (a[i+len]!=a[i+2*len+F])
这样朴素的枚举 len 后扫描整个序列是 N^2 的,我们考虑优化这个算法。
首先枚举两段的长度 len,然后我们在递推的时候可以发现,在长度为 len 时,我们没有必
要一格一格的递推,而可以每次向右递推 len 格。我们不妨设第一段的末尾位置为 i,第二
段的末尾位置为 j,设 frontL 表示 a[i+1]„a[i+len]与 a[j+1]„a[j+len]的最长公共前缀,设 backL
表示 a[i+1]„a[i+len]与 a[j+1]„a[j+len]的最长公共后缀,令 L 表示当前的最长公共后缀。
下面分两种情况考虑对于答案的贡献:
情况一:如果 L>=len,ans+=frontL。
情况二:反之,ans+=max(0,L+frontl-i+1)。
下面分两种情况考虑递推后的最长公共后缀 nL:
情况一:如果 a[i+1]„a[i+len]与 a[j+1]„a[j+len]整段相同,nL=L+len。
情况二:反之,nL=backl。
这样对于每个长度 len,需要递推 N/len 次,每次采用 hash+二分的方法 O(logN)的计算最长
公共前/后缀,总的复杂度为 O(NlnNlogN)。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
using namespace std;
typedef long long ll;
typedef unsigned int ull;

const int N=1000010;

const ull mul=133331;

ll ans;
int n,m,k;
int b[N],a[N];
int X[N];
ull Hash[N];
ull f[N]={1};

inline 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;
}

inline ull getHash(const int &l,const int &r)
{
    return Hash[r]-Hash[l-1]*f[r-l+1];
}

inline 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;
}

inline 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 main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        X[i]=a[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++)
        a[i]=bin(a[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+a[i];
    for(int i=1;2*i+m<=n;i++)
    {
        int nowl=0;
        for(int j=1;;)
        {
            int len=min(i,n-(j+i+m-1));
            if(!len) break;
            int frontl=lcp(j,j+i+m);
            int backl=anti_lcp(j+len-1,j+m+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;
    return 0;
}
posted @ 2017-04-18 19:39  qscqesze  阅读(446)  评论(0编辑  收藏  举报