CodeForces 677D Vanya and Treasure

$dp$,树状数组。

很明显这是一个$DAG$上的$dp$,由于边太多,暴力$dp$会超时,需要优化。

例如计算$dp[x][y]$,可以将区域分成四块,$dp[x][y]$取四块中的最小值,每一块用一个二维树状数组维护最小值即可。

每次扩展一层需要一个新的树状数组,因为每次初始化树状数组会超时,所以可以额外开一个数组记录一下每一个点是第几次更新的。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-6;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
    char c=getchar(); x=0;
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) {x=x*10+c-‘0‘; c=getchar();}
}

const int INF=0x7FFFFFFF;
const int maxn=310;
int n,m,p,a[maxn][maxn],c[4][maxn][maxn],d[4][maxn][maxn],dp[maxn][maxn];
vector<int>v[maxn*maxn];

int lowbit(int x){return x&(-x);}

int get(int op,int h,int x,int y)
{
    int res=INF;
    for(int i=x;i>0;i=i-lowbit(i))
        for(int j=y;j>0;j=j-lowbit(j))
            if(d[op][i][j]==h) res=min(res,c[op][i][j]);
    return res;
}

void update(int op,int h,int x,int y,int v)
{
    for(int i=x;i<=n;i=i+lowbit(i))
        for(int j=y;j<=m;j=j+lowbit(j))
        {
            if(d[op][i][j]!=h) c[op][i][j]=INF;
            d[op][i][j]=h; c[op][i][j]=min(c[op][i][j],v);
        }
}

int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            scanf("%d",&a[i][j]);
            v[a[i][j]].push_back(i*m+j);
        }

    for(int k=0;k<4;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                c[k][i][j]=INF,d[k][i][k]=-100;

    for(int i=0;i<v[1].size();i++)
        dp[v[1][i]/m+1][(v[1][i]%m)+1]=v[1][i]/m+v[1][i]%m;

    for(int i=2;i<=p;i++)
    {
        for(int j=0;j<v[i-1].size();j++)
        {
            int r=v[i-1][j]/m,c=v[i-1][j]%m; r++; c++;
            update(0,i-1,r,c,dp[r][c]-r-c);
            update(1,i-1,r,m-c+1,dp[r][c]-r+c);
            update(2,i-1,n-r+1,c,dp[r][c]+r-c);
            update(3,i-1,n-r+1,m-c+1,dp[r][c]+r+c);
        }

        for(int j=0;j<v[i].size();j++)
        {
            int r=v[i][j]/m,c=v[i][j]%m; r++; c++; dp[r][c]=INF;
            if(get(0,i-1,r,c)!=INF) dp[r][c]=min(dp[r][c],get(0,i-1,r,c)+r+c);
            if(get(1,i-1,r,m-c+1)!=INF) dp[r][c]=min(dp[r][c],get(1,i-1,r,m-c+1)+r-c);
            if(get(2,i-1,n-r+1,c)!=INF) dp[r][c]=min(dp[r][c],get(2,i-1,n-r+1,c)-r+c);
            if(get(3,i-1,n-r+1,m-c+1)!=INF) dp[r][c]=min(dp[r][c],get(3,i-1,n-r+1,m-c+1)-r-c);
        }
    }

    int ans=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(a[i][j]==p) ans=dp[i+1][j+1];
    printf("%d\n",ans);
    return 0;
}
时间: 2025-01-18 05:56:46

CodeForces 677D Vanya and Treasure的相关文章

codeforce 677D Vanya and Treasure

原题地址:http://codeforces.com/problemset/problem/677/D 题意 略 题解 直接的DP是n2m2的复杂度,据题解说通过bfs来转移可以实现n*m*sqrt(n*m)…… #include<bits/stdc++.h> #define clr(x,y) memset((x),(y),sizeof(x)) using namespace std; typedef long long LL; const int maxn=300; struct Cood

Codeforces Round #355 (Div. 2) D. Vanya and Treasure 分治暴力

D. Vanya and Treasure Vanya is in the palace that can be represented as a grid n?×?m. Each room contains a single chest, an the room located in the i-th row and j-th columns contains the chest of type aij. Each chest of type x?≤?p?-?1 contains a key

codeforces 492E. Vanya and Field(exgcd求逆元)

题目链接:codeforces 492e vanya and field 留个扩展gcd求逆元的板子. 设i,j为每颗苹果树的位置,因为gcd(n,dx) = 1,gcd(n,dy) = 1,所以当走了n步后,x从0~n-1,y从0~n-1都访问过,但x,y不相同. 所以,x肯定要经过0点,所以我只需要求y点就可以了. i,j为每颗苹果树的位置,设在经过了a步后,i到达了0,j到达了M. 则有 1----------------------(i + b * dx) % n = 0 2------

Codeforces 492E Vanya and Field(拓展欧几里得)

题目链接:Codeforces 492E Vanya and Field 通过拓展欧几里得算法求出每个位置在移动过程中,在x为0时,y的位置.统计相应y坐标最多的即为答案. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1e6 + 5; const int maxm = 1e5 + 5; typedef long long l

Codeforces Round #355 (Div. 2) Vanya and Treasure

这是一道很显然的DP题目,状态转移在题目中也很直接,就是从k-1到k,然而如果count[k-1]*cnt[k],那么时间复杂度就会很大,本来的复杂度应该是O(p*n*n*m*m),用DP的话会很TLE,看了大牛的解释后,是在p<sqrt(mn)时候用DP,之后如果p>sqrt(nm)的话就用BFS,这样用均摊分析可以计算其时间复杂度(后边我打算写一篇关于均摊分析的博文). #include <iostream> #include <cstdio> #include &

CodeForces 552C Vanya and Scales

Vanya and Scales Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 552C Description Vanya has a scales for weighing loads and weights of masses w0, w1, w2, ..., w100 grams where w is some

CodeForces - 552E Vanya and Brackets

Vanya and Brackets Time Limit: 1000MS   Memory Limit: 262144KB   64bit IO Format: %I64d & %I64u Description Vanya is doing his maths homework. He has an expression of form , where x1, x2, ..., xn are digits from 1 to 9, and sign represents either a p

Codeforces 552E Vanya and Brackets(贪心 + 表达式计算)

题目链接 Vanya and Brackets 题目大意是给出一个只由1-9的数.乘号和加号组成的表达式,若要在这个表达式中加上一对括号,求加上括号的表达式的最大值. 我们发现,左括号的位置肯定是最左端或者某个乘号右边,右括号的位置肯定是最右段或者某个乘号左边. 而乘号最多只有15个,那么暴力枚举就可以了. #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b);

codeforces 492C. Vanya and Exams 解题报告

题目链接:http://codeforces.com/problemset/problem/492/C 题目意思:给出 3 个整数:n,  r,  avg.然后有 n 行,每行有两个数:第 i 行有 ai 和 bi.表示如果写 bi 篇文章那么可以在 ai 这个分数上增加 1 分.可以增加好多次,但是前提是加完得到的分数不能超过 r.要使得 n 个exam 的分数平均分至少达到avg时需要写的最少文章是多少篇. 解决方法很简单,贪心即可. 我们当然希望写的文章越少越好,所以先对文章从小到大排序.