http://vjudge.net/contest/view.action?cid=53221#overview
UVALive 6434 - Number Assignment
problem
给你n个数,分为m组。每组的最大值减去最小值的差值加起来,最小是多少。
think
排序后。有n-1个差值。
答案就是这n-1的差值和(也是这n个数的最大值减最小值),减去m-1个最大的差值。
UVALive 6435 - Network Packet Ordering
problem
给你n,m,d,n个数表示A里面n个人到达的时间,m个数表示B里面m个人到达的时间。
如A里面第i个人的时间是ai,则他在 [ai, ai + d) 的时间到达。同理bi 。
A、B里面的人给的时间都是从小到大的,ai < a(i+1) , 同理bi 。
在一个序列里面的人是依次到达,不能交换顺序。如d = 2. ai = 1, a(i+1) = 2, 也必须i在i+1前面。同理B.
AB没有顺序关系。问有多少种顺序。
think
A把时间分为n+1个区间。
由于a<a(i+1),d<=100, 所以每个bi最多在200个区间。
dp[i][j]表示bi在它可以在的第j个区间。
转移就是:
jjj = min(R[i-1], jj) - L[i-1];
dp[i][j] = sum[i-1][jjj];
其中jj表示j表示的那个区间。R[i]表示i可以在的最后那个区间,sum[i][j] = sum(dp[i][k], k <= j)
code
const int N = 50010;
const int mod = 1000000009;
int a[N], b[N];
int L[N], R[N];
int dp[N][300];
int sum[N][300];
int main(){
int T, tt = 0;
scanf("%d", &T);
while(T--){
int n, m, d;
scanf("%d%d%d", &n, &m, &d);
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
for(int j = 0; j < m; ++j) scanf("%d", &b[j]);
for(int j = 0; j < m; ++j){
int i = 0;
if(j > 0) i = L[j-1];
L[j] = -1;
R[j] = -1;
for(; i < n; ++i){
if(b[j] >= a[i] + d) continue;
if(i > 0 && b[j] + d <= a[i-1]) break;
if(L[j] == -1) L[j] = i;
R[j] = i;
}
if(L[j] == -1) L[j] = n;
if(R[j] == -1 || b[j] + d > a[n-1]) R[j] = n;
}
for(int i = 0; i < m; ++i){
for(int j = 0, jj = L[i]; jj <= R[i]; ++j, ++jj){
if(i == 0){
dp[i][j] = 1;
continue;
}
int jjj = min(R[i-1], jj) - L[i-1];
dp[i][j] = sum[i-1][jjj];
}
sum[i][0] = dp[i][0];
for(int j = 1, jj = L[i] + 1; jj <= R[i]; ++j, ++jj){
sum[i][j] = sum[i][j-1] + dp[i][j];
if(sum[i][j] >= mod) sum[i][j] -= mod;
}
}
printf("Case #%d: %d\n", ++tt, sum[m-1][R[m-1]-L[m-1]]);
}
return 0;
}
UVALive 6436 - The Busiest City
problem
一棵树。每个点的值是这个数的任两个点path经过这个点(并且这个点不是端点)的个数。
think
设s为这个点。sz[s]表示s的子树大小。ss是s的孩子。
那么 val[s] = sum(sz[ss] * (n - 1 - sz[ss]) + (n - sz[s]) * (sz[s] - 1));
code
vector<int>v[20010];
int sz[20010];
int ans, n;
void dfs(int s, int pre){
int len = v[s].size();
sz[s] = 1;
int res = 0;
for(int i = 0; i < len; ++i){
int ss = v[s][i];
if(ss == pre) continue;
dfs(ss, s);
res += sz[ss] * (n - 1 - sz[ss]);
sz[s] += sz[ss];
}
res += (n - sz[s]) * (sz[s] - 1);
if(res > ans) ans = res;
}
int main(){
int T, tt = 0;
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i < n; i++){
int a, b;
scanf("%d%d", &a, &b);
v[a].PB(b);
v[b].PB(a);
}
ans = 0;
dfs(1, 0);
printf("Case #%d: %d\n", ++tt, ans>>1);
for(int i = 1; i <= n; ++i) v[i].clear();
}
return 0;
}
UVALive 6439 - Pasti Pas!
problem
给你一个串,可以把某些字符变成一个字符,如ABCDAB可以把AB变成a,把CD变成b,这样就变成了aba。
变完一定得是回文串。
问变完最长是多长。
think
从两头找。
如果两头找到已经可以变成一样的,那么他俩一定匹配。如AB……AB,那么AB一定变成一个字符。
因为如果他俩不变的话,也一定是AB*AB 和AB*AB为了是他俩一样。
根据这个性质,就从两边找吧。而且左边的指针i和j的话,j一直往后,找到可以的j,i和j都变成j+1,所以复杂度就是串长。
然后判断的时候hash。就可以O(1)判断了。
UVALive 6440 - Emergency Handling
problem
有n个操作。P表示进来一个病人,t0,s0,r,表示在t0时刻进来,他的危险值是s0 + r * (t - t0),t 是 >= t0的时刻。
A表示t0时刻可以抢救一个人。抢救那个危险值最大的。如果危险值一样就抢救r最大的。输出抢救的人的危险值和r。
think
r是[0, 100],所以建立101个优先队列。
把病人的危险值变为s0 - r*t0 + r*t, 优先队列里面放的是s0 - r*t0.
这样进去病人的时候,复杂度是log
抢救的时候要遍历101个队列,top操作是O(1),复杂度是101.
所以总的复杂度是T * n * max(logn, 101).
code
int main(){
int t, tt = 0;
scanf("%d",&t);
while(t--){
printf("Case #%d:\n", ++tt);
priority_queue<int,vector<int>,less<int> >q[101];
int n;
scanf("%d",&n);
while(n--){
char str[5];
int t0,s0,r,s;
scanf("%s%d", str, &t0);
if(str[0] == 'A'){
r = 0;
LL sc = 0;
for(int i = 0; i <= 100; ++i)
if(!q[i].empty()){
s = q[i].top();
LL tmp = (LL)s + i * t0;
if(tmp >= sc){
r = i;
sc = tmp;
}
}
q[r].pop();
printf("%lld %d\n", sc, r);
} else {
scanf("%d%d", &s0, &r);
q[r].push(s0 - r*t0);
}
}
}
return 0;
}
UVALive 6441 - Horrible Quiz
problem
一个人的开始分数是15000.做n道题。他自己做对的概率是w/100, 做错的概率是c/100,使用你给的答案的概率是1 - w/100 - c/100。
你要给他n个答案,最多给m个错误答案。
作对了就乘以1,做错了就乘以-1.
求他的最低分数的期望。
think
dp[i][j][2], 分别表示到第i道题已经给了j个错误答案的期望的最大值和最小值。
做对的话就是乘以 ww = (100 - 2 * c[i+1]) / 100.; 做错就是乘以 cc = (2 * w[i+1] - 100) / 100.;
code
int w[N], c[N];
double f[N][N][2];
int n, m;
int main(){
int T, tt = 0;
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
for(int i = 1; i <= n; ++i) scanf("%d", &c[i]);
for(int i = 0; i <= n; ++i) for(int j = 0; j <= m; ++j){
f[i][j][0] = 15000;
f[i][j][1] = -15000;
}
f[0][0][0] = 15000;
f[0][0][1] = 15000;
for(int i = 0; i < n; ++i){
for(int j = 0; j <= i && j <= m; ++j){
double ww = (100 - 2 * c[i+1]) / 100.;
double cc = (2 * w[i+1] - 100) / 100.;
f[i+1][j][0] = min(f[i+1][j][0], f[i][j][0] * ww);
f[i+1][j][0] = min(f[i+1][j][0], f[i][j][1] * ww);
f[i+1][j][1] = max(f[i+1][j][1], f[i][j][0] * ww);
f[i+1][j][1] = max(f[i+1][j][1], f[i][j][1] * ww);
f[i+1][j+1][0] = min(f[i+1][j+1][0], f[i][j][0] * cc);
f[i+1][j+1][0] = min(f[i+1][j+1][0], f[i][j][1] * cc);
f[i+1][j+1][1] = max(f[i+1][j+1][1], f[i][j][0] * cc);
f[i+1][j+1][1] = max(f[i+1][j+1][1], f[i][j][1] * cc);
}
}
double ans = 15000;
for(int j = 0; j <= m; ++j) ans = min(ans, f[n][j][0]);
printf("Case #%d: %.3f\n", ++tt, ans);
}
return 0;
}
UVALive 6442 - Coins on a Ring
problem
一个环,n个位置,有m个人,m被n整除。现在要把m个人移动位置使他们的间距相同。
消耗的能量是移动的最多的那个人的移动了多少。
求最少消耗的能量。
think
排序后让他们依次对应某个可以的序列,如0, n/m, 2*n/m ……
然后依次得到差值,最大差值和最小差值的中间的数就是答案。。
code
int a[20100];
int main(){
int T, tt = 0;
scanf("%d", &T);
while(T--){
int n, m, k;
scanf("%d%d", &n, &m);
k = n / m;
int mi = n;
int mx = -n;
for(int i = 0; i < m; ++i) scanf("%d", &a[i]);
sort(a, a + m);
for(int i = 0; i < m; ++i){
int pos = k * i - a[i];
mi = min(mi, pos);
mx = max(mx, pos);
}
printf("Case #%d: %d\n", ++tt, (mx - mi + 1) / 2);
}
return 0;
}