http://codeforces.com/contest/360
A. Levko and Array Recovery
从后往前根据操作给每个位置赋值,然后还要从前往后判断操作是否可行。
// Author : JayYe Created Time: 2013-11-12 20:29:06
#include
#include
#include
using namespace std;
typedef __int64 ll;
const int maxn = 5000 + 5;
struct PP {
int op, l, r, v;
}a[maxn];
bool vis[maxn];
ll ans[maxn], pr[maxn];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1;i <= m; i++)
scanf("%d%d%d%d", &a[i].op, &a[i].l, &a[i].r, &a[i].v);
for(int i = m;i >= 1; i--) {
if(a[i].op == 1) {
for(int j = a[i].l;j <= a[i].r; j++) if(vis[j])
ans[j] -= a[i].v;
}
else {
for(int j = a[i].l;j <= a[i].r; j++) {
if(!vis[j]) {
vis[j] = true;
ans[j] = a[i].v;
}
else if(ans[j] >= a[i].v){
ans[j] = a[i].v;
}
}
bool ok = 0;
for(int j = a[i].l;j <= a[i].r; j++) if(ans[j] == a[i].v) {
ok = 1; break;
}
if(!ok) return puts("NO"), 0;
}
}
for(int i = 1;i <= n; i++) {
if(!vis[i])
ans[i] = 0;
pr[i] = ans[i];
}
bool ok = 1;
for(int i = 1;i <= m; i++) {
if(a[i].op == 1) {
for(int j = a[i].l;j <= a[i].r; j++)
ans[j] += a[i].v;
}
else {
ll mx = ans[a[i].l];
for(int j = a[i].l;j <= a[i].r; j++)
mx = max(mx, ans[j]);
if(mx != a[i].v) return puts("NO"), 0;
}
}
puts("YES");
for(int i = 1;i <= n; i++)
printf("%I64d ", pr[i]);
puts("");
return 0;
}
B. Levko and Array
给你n个数,最多可以改变k个数,使得val = max( a(i), a(i+1) )尽量小,最小是多少。 n <= 2000
思路:首先考虑高复杂度的,设dp[i][j]表示 i 这个位置不变,前面已经改变了j个数最小的val值。那么 dp[i][j]可以由 dp[k][j-1] k < i转移过来,表示i位置不变k位置也不变中间的随意变,所以 dp[i][j] = min( dp[k][j-1] + i-j-1) ,每个转移是O(n)的,总复杂度是O(n^3)。
二分答案X,复杂度就降低到了O(n^2logn)。。就done了
// Author : JayYe Created Time: 2013-11-13 11:14:35
#include
#include
#include
using namespace std;
typedef __int64 ll;
const int maxn = 2000 + 5;
const ll INF = 1LL<<31;
int n, k;
ll a[maxn], dp[maxn];
ll ABS(ll x) {
return x > 0 ? x : -x;
}
bool doubi(ll d) {
dp[1] = 0;
for(int i = 2;i <= n; i++) {
dp[i] = INF;
for(int j = i-1;j >= 1; j--) {
if((ABS(a[i] - a[j]) + i-j-1)/(i-j) <= d)
dp[i] = min(dp[i], dp[j] + i-j-1);
}
if(i - 1 <= k) dp[i] = min(dp[i], i-1LL);
}
for(int i = 1;i < n; i++)
dp[n] = min(dp[n], dp[i] + n-i);
return dp[n] <= k;
}
int main() {
scanf("%d%d", &n, &k);
for(int i = 1;i <= n; i++)
scanf("%I64d", &a[i]);
ll l = 0, r = INF;
while(l < r) {
ll mid = (l + r)/2;
if(doubi(mid)) r = mid;
else l = mid+1;
}
printf("%I64d\n", r);
return 0;
}
C. Levko and Strings
题意自己理解。。
思路: 设dp[i][j]表示i这个位置不等于s[i]的产生的值为j的情况数。
dp[i][j]一个转移是由前一个与原串字母不一样的转移过来的。则
如果上一个不一样的字母在cur这个位置,那么如果t[i] > s[i],可以产生 (n-i+1)*(i-cur)的值。
转移的复杂度是O(n*logn)的,可以自己好好想想。具体见代码
// Author : JayYe Created Time: 2013-11-13 15:45:52
#include
#include
#include
using namespace std;
typedef __int64 ll;
const int maxn = 2000 + 5;
const int mod = 1000000007;
char s[maxn];
ll dp[maxn][maxn], sum[maxn];
int main() {
int n, k;
scanf("%d%d", &n, &k);
scanf("%s", s + 1);
dp[0][0] = 1;
sum[0] = 1;
for(int i = 1;i <= n; i++) {
for(int j = 0;j <= k; j++)
dp[i][j] = sum[j]*(s[i]-'a');
int cur = i-1;
while(cur >= 0 && (i-cur)*(n-i+1) <= k) {
for(int j = 0;j <= k; j++) {
if(j + (i-cur)*(n-i+1) > k) break;
dp[i][j+(i-cur)*(n-i+1)] = (dp[i][j+(i-cur)*(n-i+1)]+dp[cur][j]*('z'-s[i]))%mod;
}
cur--;
}
for(int j = 0;j <= k; j++)
sum[j] = (sum[j] + dp[i][j])%mod;
}
printf("%I64d\n", sum[k]);
return 0;
}