您的位置:首页 > 运维架构

Topcoder Srm 649 DIV1

2015-02-11 16:49 309 查看
Srm 649 250pt Decipherability

题意:给出一个长度<= 50的字符串S,和一个数K,问从中删除K个字符后,得到的结果是否一定不产生歧义,即任意删除K个字符,不会有重复的结果出现。

思路,K = len(S)的时候,是唯一确定的。 当K不等于len(S)的时候,说明要保留len(S) - K个字符,枚举保留前i个字符和后j个字符,且 i + j == len(S) - K, 然后S[i + 1,j - 1]取一个字符,这一段肯定不能有重复字符出现,若有,一定有重复,否则,答案是唯一

Srm 649 550pt XorSequence

题意:给出一个长度<= 2^17 的序列A,序列中的每个数范围是[0,N - 1], N <= 20^30,你可以选择任意一个整数B,将A中的每个数都异或上B,得到序列C(C[i] = A[i] ^ B)。问在恰当选择B的情况下,最多有多少个关系对(C[i],C[j]),其中i < j 且C[i] < C[j]

思路:异或涉及位运算,将每个数 A[i] 转化成二进制表示形式,对于前缀相同的数,B的相同长度前缀异或对结果无影响。可以将每个数都插入到一个trie树中。trie中的节点记录几个信息,一个是下一个分支为1或0的数的个数,另一个是当B这个数取1或0的时候,后缀中C[i] < C[j]的对数,这样的意义是从根节点到当前节点,前缀是相同的,前面说过前缀相同的话,B的相同长度前缀对结果无影响,对于第一个不相同的最高位,看看B对应的这一位取0或者取1的满足条件(C[I] < C[J])的数对,因为数是从前往后插入的,所以每来一个点统计一次的话,i
< j这个条件是已经保证了的。 然后对于每个深度,将trie中这个深度的所有为0或所有为1的加起来,和最大的就是相同前缀下,下一位取0或取1的最大值,分位(深度)求和即可。

#include <bits/stdc++.h>
using namespace std;

struct trie
{
trie *next[2];
long long val[2];
int cnt[2];
trie() {
next[0] = next[1] = NULL;
val[0] = val[1] = cnt[0] = cnt[1] = 0;
}
}e[(1 << 17) * 30 + 100],*root;

int cnt = 0;
void insert(int x)
{
trie *p = root;
for(int i = 29; i >= 0; i --) {
int idx = (x & (1 << i)) ? 1 : 0;
p->cnt[idx] ++;
p->val[idx] += p->cnt[idx ^ 1];
if(!p->next[idx]) p->next[idx] = &e[cnt ++];
p = p->next[idx];
}
};

long long c[35][2];

void dfs(trie *root,int dep)
{
c[dep][0] += root->val[0];
c[dep][1] += root->val[1];
if(root->next[0]) dfs(root->next[0],dep - 1);
if(root->next[1]) dfs(root->next[1],dep - 1);
}

struct XorSequence {
long long getmax(int N, int sz, int A0, int A1, int P, int Q, int R) {
root = &e[cnt ++];
insert(A0);
insert(A1);
for(int i = 2; i < sz; i ++) {
int A2 = (1LL * A0 * P + 1LL * A1 * Q + R) % N;
insert(A2);
A0 = A1;
A1 = A2;
}
dfs(root,30);
long long ans = 0;
for(int i = 1; i <= 30; i ++) {
ans += max(c[i][0],c[i][1]);
}
return ans;
}
};


Srm 649 850pt CartInSupermarket

题意:给出一堆数,数目不大于50个,每个数的范围是[0,10^9],再给出一个数b。对于每分钟,对于每个大于0的数a,可以同时进行以下两个操作之一:(1)将这个数减去1;(2)将一个分成两个整数x,y,其中a = x + y。限制只有一个,就是(2)操作总数不能超过b. 问在最优的操作情况下,使所有数变为0的最少需要多少分钟。

思路:对于一个数来说,肯定是先进行若干次(2)操作,然后再均分进行(1)操作的。(2)操作总数越多,显然是越有可能减少时间的(总操作数b未必要用完)。可以选择二分时间,b与时间显然是具有单调关系,因此我们可以算出每个数在特定需要的时间下,最少使用(2)的操作次数,其总和如果<=b,说明这个时间点是可以的,否则需要更多的时间。

二分最少需要的时间time后,现在的问题变成了对于一个数a,使得它在不超过time的时间内变为0,至少要分成多少块。首先要明确一个概念,因为是并行操作的,所以对于一个数来说,肯定是尽量均分的,这个均分的话,是在一个满二叉树的叶子上进行。由于满二叉树每一次拓展出比前面多一倍的叶子节点,所以可以枚举枚举最终分成多少块(即叶子节点个数),然后算出满二叉树的深度(即叶子节点数>=块数需要的时间),然后对于剩余的节点均分,剩余最多的是((总数 + 块数 - 1) / 块数,+块数-1即不整除的时候,有至少一个节点数量是总数/块数+1),这个最终分成多少块,在块数不大于节点数的时候也是具有单调性的,所以这一步可以选择二分分成多少块。
计算的时候还有一个需要注意的,因为满二叉树的叶子节点未必要用完,可能将一些点移动到上一层,这样叶子总数也是够的,然后上一层分配的每个叶子节点,在其他点进行最后一次分裂的时候,它们可以进行一次(1)操作,所以要将总数 - 进行(1)操作的数目,然后再进行均分。

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<int>::iterator vit;

struct CartInSupermarket {

map<long long,int> ma;
long long calc_min_time(int a,long long b) {
if(b == 1) return a;
int i = 0;
while((1LL << i) < b) i ++;
a -= (1LL << i) - b;
return i + (a + b - 1) / b;
}

long long calc_min_split(int a,int time) {
if(!time && a) return 1LL << 50;
long long lt = 1,rt = a + 1,mid,ans = 1LL << 50;
while(lt <= rt) {
mid = lt + rt >> 1;
if(calc_min_time(a,mid) <= time) ans = mid,rt = mid - 1;
else lt = mid + 1;
}
return ans - 1;
}

int calcmin(vector <int> a, int b) {
long long lt = 0,rt = *max_element(a.begin(),a.end());
long long ans = 0;
while(lt <= rt) {
long long mid = lt + rt >> 1;
long long sum = 0;
for(int i = 0; i < a.size(); i ++)
sum += calc_min_split(a[i],mid);
if(sum <= b) ans = mid,rt = mid - 1;
else lt = mid + 1;
}
return ans;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: