Codeforces Round #613 (Div. 2)
https://codeforces.com/contest/1285
A - Mezo Playing Zoma
题意:按一系列的"LR"键,其中一些键可能被忽略执行,求最后分布的位置数量。
题解:肯定是最左和最右夹着的区间,所以统计最左的和最右的位置。最后会发现恰好就是n+1。
char s[1000005];
void test_case() {
int n;
scanf("%d%s", &n, s + 1);
int cntl = 0, cntr = 0;
for(int i = 1; i <= n; ++i) {
if(s[i] == 'L')
++cntl;
else
++cntr;
}
printf("%d\n", cntl + cntr + 1);
}
B - Just Eat It!
题意:问是否有一段subsegment的和>=全体的和,这个subsegment不能取全体。
题解:这题里的subsegment要去掉非空前缀,或者去掉非空后缀,或者两个都去掉。假如非空前缀中有<=0的,那么就去掉这段就可以了。非空后缀同理。
看了一下qls的写法,貌似可以直接dp转移出最大subsegment和(确实是dp的入门题),设dp[i]为以i位置结尾的最大subsegment和,那么就有
dp[i]=max(0,dp[i-1])+a[i];
最后的答案就是所有dp[i]的最大值。
ll a[100005];
ll prefix[100005];
ll suffix[100005];
void test_case() {
int n;
scanf("%d", &n);
suffix[n + 1] = 0;
for(int i = 1; i <= n; ++i)
scanf("%lld", &a[i]);
ll minpre = LINF, minsuf = LINF;
for(int i = 1; i <= n; ++i) {
prefix[i] = prefix[i - 1] + a[i];
minpre = min(minpre, prefix[i]);
}
if(minpre <= 0) {
puts("NO");
return;
}
for(int i = n; i >= 1; --i) {
suffix[i] = suffix[i + 1] + a[i];
minsuf = min(minsuf, suffix[i]);
}
if(minsuf <= 0) {
puts("NO");
return;
}
puts("YES");
}
C - Fadi and LCM
题意:给一个X,求LCM(a,b)==X的最小的max(a,b)。
题解:分解X,取最后一对互质的a*b=X。
首先显然可以知道其中一个a是X的因子,然后b=X/a,若a和b互质那么LCM(a,b)==X。否则设a和b有一个GCD为d,d>1,这样不妨把b里的d给除掉,b会变小,而LCM并不变。
ll X, a, b;
void update(ll ta, ll tb) {
if(max(a, b) > max(ta, tb)) {
a = ta;
b = tb;
}
}
void test_case() {
scanf("%lld", &X);
if(X == 1ll) {
puts("1 1");
return;
}
a = 1;
b = X;
for(ll i = 2; i * i <= X; ++i) {
if(X % i == 0) {
ll ta = i;
ll tb = X / i;
ll tg = __gcd(ta, tb);
if(tg == 1)
update(ta, tb);
}
}
printf("%lld %lld\n", a, b);
}
D - Dr. Evil Underscores
题意:找一个X,使得序列a中的所有数异或X之后的结果的最大值最小,输出这个最小的最大值。
题解:首先异或很好想的就是要分解各位,但是这题里面各位之间不独立,需要从最高位开始往下贪心。显然若某位的值全部是0或者全部是1,那么可以正确赋值使得这位为0,否则这一位一定是1。不过这种情况要分赋值为0和赋值为1两种进行讨论,保留了其中一种之后另一种会因为失去了最高位而从最大值的角逐中落败,在低位的安排中可以不再考虑。所以弄个trie树进行dp,小心越界等边界条件。
struct TrieNode {
int nxt[2];
void Init() {
memset(nxt, 0, sizeof(nxt));
}
};
struct Trie {
static const int MAXN = 100500 * 35;
TrieNode tn[MAXN + 5];
int root, top;
int NewNode() {
tn[++top].Init();
return top;
}
void Init() {
top = 0;
root = NewNode();
}
void Insert(int *a, int len) {
int cur = root;
for(int i = 0; i <= len; ++i) {
int &nxt = tn[cur].nxt[a[i]];
if(!nxt)
nxt = NewNode();
cur = nxt;
}
}
int dfs(int id, int dep) {
if(tn[id].nxt[0] == 0 && tn[id].nxt[1] == 0) {
return 0;
} else if(tn[id].nxt[0] == 0 || tn[id].nxt[1] == 0) {
if(tn[id].nxt[0] == 0)
return dfs(tn[id].nxt[1], dep + 1);
else
return dfs(tn[id].nxt[0], dep + 1);
} else {
int res = min(dfs(tn[id].nxt[0], dep + 1), dfs(tn[id].nxt[1], dep + 1));
//cout<<"id="<<id<<" dep="<<dep<<endl;
//cout<<"bitset="<<bitset<30>(res|(1<<(29-dep)))<<endl;
return (res | (1 << (29 - dep)));
}
}
void DP() {
printf("%d\n", dfs(root, 0));
}
} trie;
int a[35];
void test_case() {
int n;
scanf("%d", &n);
trie.Init();
while(n--) {
int ai;
scanf("%d", &ai);
for(int i = 0; i < 30; ++i)
a[29 - i] = (ai >> i) & 1;
trie.Insert(a, 29);
}
trie.DP();
}
E - Delete a Segment
题意:给n条线段,恰好删除其中的一条,使得剩下的线段合并之后的数量最大,求此数量。