zoukankan      html  css  js  c++  java
  • IOI 2013 袋熊(线段树+分块+决策单调性)

    题意

    [http://www.ioi2013.org/wp-content/uploads/tasks/day1/wombats/Wombats zh (CHN).pdf](http://www.ioi2013.org/wp-content/uploads/tasks/day1/wombats/Wombats zh (CHN).pdf)

    思路

    ​ 我们设矩形的行数为 (n(5000)) ,列数为 (m(200)) ,更新次数为 (U(500)) ,查询次数为 (Q(2 imes 10^5))

    ​ 最暴力的思想是一次更新处理出从一个 (m^2) 的数组,代表第一行的每一个点走到最后一行每一个点的最小代价,然后 ({cal O}(1)) 回答。处理出这样一个数组的代价是 ({cal O}(nm^2)) 的,于是我们得到了一个 ({cal O}(Unm^2+Q)) 的做法。

    ​ 考虑到我们可以直接把上述二维数组看成矩阵,从而 (cal O(m^3)) 的合并两个二维数组,那么在假如没有空间限制,我们就可以在 (n) 那一维上套线段树,从而做到 ({cal O}(nm^3+Um^3log n+Q)) 的时间复杂度。

    ​ 再仔细观察一下题设,我们发现在作矩阵乘法的时候。(C(i,j)leftarrow displaystylemin_k{ A(i,k)+B(k,j)}) 随着 (i) 变大, 最后起更新作用的 (k) 变大;随着 (j) 变大, 最后起更新作用的 (k) 也变大。于是我们发现了决策单调性,一次矩阵乘法的复杂度降到了 ({cal O}(m^2)) 。这是本题最关键的结论,现在复杂度被降到了({cal O}(nm^2+Um^2log n+Q))

    ​ 这个算法在时间上已经没有问题了,但是空间开不下,于是我们想到了缩减 (n) 。不妨先对原序列进行分块,再以块为基本元素维护线段树即可。块内就直接用最开始讲的暴力做法 ({cal O}(nm^2)) 的更新(当然你也可以矩乘),线段树的 ( ext{push_up}) 操作就是 ({cal O}(m^2)) 的矩乘。

    ​ 设块大小是 (S) ,最终时间复杂度为 (displaystyle {cal O}{ig(}nm^2+Um^2(S+log{nover S})+Q{ig )}) ,空间复杂度为 (displaystyle {cal O}(nm+{nover S}m^2)) 。块大小越小越好,只要不炸空间。

    代码

    #include<bits/stdc++.h>
    #define FOR(i, x, y) for(register int i = (x), i##END = (y); i <= i##END; ++i)
    #define DOR(i, x, y) for(register int i = (x), i##END = (y); i >= i##END; --i)
    template<typename T, typename _T> inline bool chk_min(T &x, const _T &y) {return y < x ? x = y, 1 : 0;}
    template<typename T, typename _T> inline bool chk_max(T &x, const _T &y) {return x < y ? x = y, 1 : 0;}
    typedef long long ll;
    const int N = 5005;
    const int M = 205;
    const int S = 7;
    int R[N][M], D[N][M];
    int dp[(N / S + 5) << 2][M][M];
    int ans[M][M]; bool mark;
    int n, m, q;
    
    inline int bid(int k) {return k / S;}
    inline int bl(int k) {return std::max(1, k * S);}
    inline int br(int k) {return std::min(n, (k + 1) * S - 1);}
    
    void mul(int C[M][M], int A[M][M], int B[M][M])
    {
    	static int s[M][M];
    	FOR(x, 0, m + 1) FOR(y, 0, m + 1) s[x][y] = -1;
    	FOR(x, 1, m)
    		DOR(y, m, 1)
    		{
    			C[x][y] = 2e9;
    			int l = 1, r = m;
    			if(~s[x - 1][y]) chk_max(l, s[x - 1][y]);
    			if(~s[x][y + 1]) chk_min(r, s[x][y + 1]);
    			FOR(z, l, r)
    				if(chk_min(C[x][y], A[x][z] + B[z][y]))
    					s[x][y] = z;
    		}
    }
    
    void construct(int x, int dp[M][M])
    {
    	int u = bl(x), d = br(x);
    	FOR(i, 1, m)
    	{
    		dp[i][i] = 0;
    		int sum = 0;
    		DOR(j, i - 1, 1)
    		{
    			sum += R[u][j];
    			dp[i][j] = sum;
    		}
    		sum = 0;
    		FOR(j, i + 1, m)
    		{
    			sum += R[u][j - 1];
    			dp[i][j] = sum;
    		}
    		FOR(j, 1, m) dp[i][j] += D[u][j];
    
    		FOR(x, u + 1, d)
    		{
    			FOR(j, 2, m) chk_min(dp[i][j], dp[i][j - 1] + R[x][j - 1]);
    			DOR(j, m - 1, 1) chk_min(dp[i][j], dp[i][j + 1] + R[x][j]);
    			FOR(j, 1, m) dp[i][j] += D[x][j];
    		}
    	}
    }
    
    void build(int k, int l, int r)
    {
    	if(l == r)
    	{
    		construct(l, dp[k]);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(k << 1, l, mid);
    	build(k << 1 | 1, mid + 1, r);
    	mul(dp[k], dp[k << 1], dp[k << 1 | 1]);
    }
    
    void update(int k, int x, int l, int r)
    {
    	if(l == r)
    	{
    		construct(l, dp[k]);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(x <= mid) update(k << 1, x, l, mid);
    	else update(k << 1 | 1, x, mid + 1, r);
    	mul(dp[k], dp[k << 1], dp[k << 1 | 1]);
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	FOR(i, 1, n) FOR(j, 1, m - 1) scanf("%d", &R[i][j]);
    	FOR(i, 1, n - 1) FOR(j, 1, m) scanf("%d", &D[i][j]);
    
    	build(1, bid(1), bid(n));
    
    	scanf("%d", &q);
    	while(q--)
    	{
    		int cmd, u, v, w;
    		scanf("%d%d%d", &cmd, &u, &v);
    		u++, v++;
    		if(cmd == 1)
    		{
    			scanf("%d", &w);
    			R[u][v] = w;
    			update(1, bid(u), bid(1), bid(n));
    		}
    		else if(cmd == 2)
    		{
    			scanf("%d", &w);
    			D[u][v] = w;
    			update(1, bid(u), bid(1), bid(n));
    		}
    		else if(cmd == 3)
    			printf("%d
    ", dp[1][u][v]);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    centos 安装 谷歌BBR
    centos上mailx通过465端口发送邮件
    centos 6的LAMP一键安装包(可选择/升级版本)
    Linux 一键安装最新内核并开启 BBR 脚本
    CentOS 7安装Xfce和VNC
    重构手法之简化函数调用【4】
    重构手法之简化函数调用【3】
    重构手法之简化函数调用【2】
    重构手法之简化函数调用【1】
    重构手法之简化条件表达式【4】
  • 原文地址:https://www.cnblogs.com/Paulliant/p/12047771.html
Copyright © 2011-2022 走看看