SRM 582

250:最大的最小,最小的最大之类的题。。二分+验证

600:是个好题,整了好几天才整明白。

题意: 给你n个数1 2 3。。n,代表n个楼,每个数有一种颜色,数值就代表building的高度,现在问有多少的排列满足从左往右看能看到L种颜色。

n <= 1300,显然复杂度应该是平方级别的。

考虑从小到大一个个的占坑,dp[i][j]表示前i个数占完坑后,能看到j种颜色的方案数

显然,一个状态若是合法,应该使得后面的数插进来后是这个状态的延续,比如i j 这两维都应该是非递减的,其实就是i这个数前面不应该有空位了,否则这个空位会被后面的比较大的数占据,然后就破坏了原来的这个状态。

转移起来就是

dp[i][j] = dp[p][?] * A(n - 1 - p, i - p - 1);

用部分和来优化这个dp即可,注意dp[i][1]状态的初始化。

#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <ctime>
using namespace std;

class ColorfulBuilding
{
public: 
    int count(vector <string> color1, vector <string> color2, int L);
    
    

};

const int N = 1300;
const int mod = 1000000009;
int dp[N][N], cid[N], col[N], val[N][N], sum[N];
int P[N], inv_P[N];
int power_mod(long long a,long long b,long long c) {
  long long res = 1;
  while(b > 0) {
    if(b & 1) res = res * a % c;
    b >>= 1; a = a * a % c;
  }
  return (int) res;
}
inline void Add(int &x,int y) {
  x += y;
  if(x >= mod) x -= mod;
}
int ColorfulBuilding::count(vector <string> color1, vector <string> color2, int L){
  string s1 = "", s2 = "";
  for(int i = 0; i < color1.size(); i++) s1 += color1[i];
  for(int i = 0; i < color2.size(); i++) s2 += color2[i];
  map<int,int> mp;
  int totColor = 0;
  for(int i = 0; i < s1.length(); i++) {
    col[i + 1] = s1[i] * 256 + s2[i];
    if(mp[col[i + 1]] == 0) mp[col[i + 1]] = ++totColor;
    col[i + 1] = mp[col[i + 1]];
  }
  int n = s1.length(); 
  inv_P[0] = 1; P[0] = 1;
  for(int i = 1; i <= n; i++) {
    P[i] = 1LL * P[i - 1] * i % mod;
    inv_P[i] = 1LL * inv_P[i - 1] * power_mod(i, mod - 2, mod) % mod;
  }
  memset(dp, 0, sizeof(dp));
  memset(val, 0, sizeof(val));
  memset(sum, 0, sizeof(sum));
  for(int i = 1; i <= n; i++) {
    for(int j = 1; j <= i && j <= L; j++) {
      int add;
      if(j == 1) {
        add = 1LL * P[n - 1] * inv_P[n - i] % mod;
        Add(add, 1LL * val[col[i]][j] * inv_P[n - i] % mod);
      } else {
        add = sum[j - 1] - val[col[i]][j - 1];
        if(add < 0) add += mod;
        Add(add, val[col[i]][j]);
        add = 1LL * add * inv_P[n - i] % mod;
      }
      dp[i][j] = add;
      if(i < n) {
        add = 1LL * dp[i][j] * P[n - i - 1] % mod;
        Add(sum[j], add);
        Add(val[col[i]][j], add);
      }
    }
  }
  return dp[n][L];
}


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值