zoukankan      html  css  js  c++  java
  • COCI2011/2012 Contest#1 F 状压加速dp

    COCI2011/2012 Contest#1 F 状压加速dp

    首先是一个非常Naive的dp,令(dp[i][x][y])表示(i)时刻(x,y)是否能被跳到

    枚举,然后转移,如果滚动数组,就可以做到(O(n^2))空间,(O(Tn^2))时间复杂度

    这显然是TLE的。。。

    [ ]

    注意到题目的(nleq 30),可以直接用一个int存在某一行/列的答案

    设时刻(i)(j)列的答案为(dp[i][j])

    假设不考虑答案的限制,两之间转移可以做到(O(1)),即

    1.(dp[i][jpm 1])左/右移两位

    2.(dp[i][jpm 2])左/右移一位

    两者转移即可,但是涉及到倍数的限制,设(can[i][j])(i)时刻(j)列的可行跳跃位置

    则只需要最后的时候让(dp[i][j])(can[i][j])取交集即可

    如果直接枚举倍数,复杂度上限是(O(n^2 T))

    考虑分块决策,设将([1,D])的因数挑出来额外记录一个数组(can2[x][j])表示值为(x)的第(j)列有那些

    不直接枚举他们,而是在每次访问时考虑他们对于(can[i][j])的贡献

    在优秀实现下,复杂度上限为(O(n^2frac{T}{D+1}+T (D+sum_{t=1}^{D} frac{1}{t} n))=O(n^2frac{T}{D+1}+T (nln D+D)))

    这个实现上来说,就是枚举时间(i)后,判断是否满足(t|i),然后再将(can2[t][j])贡献到(can[i][j])

    显然,(t|i)成立的次数就是(Tsum_{t=1}^{D} frac{1}{t}),也就是要循环这么多次取贡献(j)这一维

    调整一下(D)的参数

    #include<bits/stdc++.h>
    using namespace std;
    #pragma GCC optimize(2)
    #define reg register
    #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
    const int N=30,D=7;
    
    int n,m,a[N][N],dp[2][N];
    int can[1000010][N],t[D+1][N];
    
    int main(){
    	scanf("%d%d",&n,&m);
    	int sx,sy; scanf("%d%d",&sx,&sy),sx--,sy--;
    	rep(i,0,n-1) rep(j,0,n-1) {
    		scanf("%d",&a[i][j]);
    		if(a[i][j]<=D) t[a[i][j]][i]|=1<<j;
    		else for(reg int T=a[i][j];T<=m;T+=a[i][j]) can[T][i]|=1<<j;
    	}
    	int cur=0; dp[cur][sx]=1<<sy;
    	rep(i,1,m) {
    		rep(j,1,D) if(i%j==0) rep(k,0,n-1) can[i][k]|=t[j][k];
    		rep(j,0,n-1) {
    			dp[!cur][j]=0;
    			if(j) {
    				dp[!cur][j]|=dp[cur][j-1]<<2;
    				dp[!cur][j]|=dp[cur][j-1]>>2;
    			}
    			if(j<n-1) {
    				dp[!cur][j]|=dp[cur][j+1]<<2;
    				dp[!cur][j]|=dp[cur][j+1]>>2;
    			}
    			if(j>1) {
    				dp[!cur][j]|=dp[cur][j-2]<<1;
    				dp[!cur][j]|=dp[cur][j-2]>>1;
    			}
    			if(j<n-2) {
    				dp[!cur][j]|=dp[cur][j+2]<<1;
    				dp[!cur][j]|=dp[cur][j+2]>>1;
    			}
    			dp[!cur][j]&=can[i][j];
    		}
    		cur^=1;
    	}
    	int ans=0;
    	rep(i,0,n-1) rep(j,0,n-1) if(dp[cur][i]&(1<<j)) ans++;
    	printf("%d
    ",ans);
    	rep(i,0,n-1) rep(j,0,n-1) if(dp[cur][i]&(1<<j)) printf("%d %d
    ",i+1,j+1);
    }
    
    
    
    
    
    
    
  • 相关阅读:
    快速幂 --- CSU 1556: Jerry's trouble
    iOS masonry 不规则tagView布局 并自适应高度
    iOS 渐变色实现,渐变色圆环,圆环进度条
    iOS scrollView中嵌套多个tableView处理方案
    iOS 多网络请求同步并发
    iOS UIView添加阴影
    什么是比特币,什么是区块链,什么是挖矿,为什么要挖?关于比特币,关于区块链,你想要知道的
    Xcode wifi连接真机调试
    iOS 添加WKWebView导致控制器无法释放的问题
    解决百度网盘非会员下载速度限制的完整方案
  • 原文地址:https://www.cnblogs.com/chasedeath/p/13573629.html
Copyright © 2011-2022 走看看