TopCoder 649 div1 & div2
最近一场TC,做得是在是烂,不过最后challenge阶段用一个随机数据cha了一个明显错误的代码,最后免于暴跌rating,还涨了一点。TC题目质量还是很高的,非常锻炼思维,拓展做题的视野,老老实实补题吧。
div2
250pts
题意:判断s去掉一个字符后能否和t一样。
代码:
class DecipherabilityEasy {
public:
string check(string s, string t) {
int n = s.size();
int m = t.size();
if(n- != m) return "Impossible";
for(int i = ; i < n; i++){
string tmp = "";
for(int j = ; j < i; j++)
tmp += s[j];
for(int j = i+; j < n; j++)
tmp += s[j];
if(tmp == t) return "Possible";
}
return "Impossible";
}
};
500pts
题意:给定一个长度为n的序列,1 <= n <= 100,可以有两种操作,每次可以将序列从任意位置分成两部分,或者让序列长度减少1,每个操作占1min,每一轮中可以对每个序列进行一次以上操作,问是所有序列消失最短时间,其中split操作的次数不超过k。
分析:dp[n][k]表示对长度为n的序列,最多进行k次split操作时,最短时间。
可以得到以下转移:
dp[n][k] = min(dp[n][k], max(dp[i][j], dp[n-i][k-1-j])+1);
dp[n][k] = min(dp[n][k], n).
代码:
int dp[][];
class CartInSupermarketEasy {
public: int calc(int N, int K) {
memset(dp, -, sizeof dp);
for(int i = ; i <= N; i++)
dp[i][] = i;
for(int i = ; i <= K; i++)
dp[][i] = ;
// cout << dp[0][0] << endl;
// cout << (~0) << endl;
return dfs(N, K);
}
int dfs(int x, int y) {
// if(x == 0) return 0;
// if(y == 0) return x;
int &res = dp[x][y];
// cout << dp[x][y] << endl;
// cout << res << endl;
// cout << x << y << endl;
// cout << res << endl;
if(~res) return res;
res = x;
res = min(res, dfs(x-, y) + );
y--;
for(int i = ; i < x; i++)
for(int j = ; j <= y; j++) {
res = min(res, max(dfs(i,j), dfs(x-i, y-j))+);
}
// if(res == 0) cout << x << y << endl;
return res;
}
};
解题关键:发现split操作后得到的子序列问题是原问题的子问题。
1000pts
题意:数组A[i],长度n <= 50,求C,令B[i] = A[i]^C,使得B[i]中满足i < j,B[i] < B[j]的数对最多,输出这样的最大值。
分析:考虑两个数A[i],A[j]异或一个数C的大小关系,考虑两数的二进制位,设从第k位开始,A[i],A[j]二进制位不同,也就是k+1, k+2及高位都完全相同,设k位(x, 1-x),那么异或后数B[i],B[j]大小主要与(x,1-x)及C的第k位(记做y)有如下关系:
x = 0,y = 1,B[i] > B[j]; x = 0,y = 0,B[i] < B[j];
x = 1,y = 1,B[i] < B[j]; x = 1,y = 0,B[i] > B[j].
此题n不大,枚举第k位,确定第k位为0,1时,能够根据第k位为0或1确定的满足条件i < j,且B[i] < B[j]的对数,取两者中最大值。
代码:
class XorSequenceEasy {
public:
int getmax(vector <int> A, int N) {
int ans = , B = ;
int n = sz(A);
N = __builtin_popcount(N-);
// cout << N << endl;
for(int i = ; i < N; i++) {
int tmp[];
tmp[] = tmp[] = ;
for(int k = ; k < n; k++)
for(int l = k+; l < n; l++) {
if(((A[k]^A[l])>=(<<i)) &&
((A[k]^A[l])<(<<(i+))))
tmp[(A[k]>>i)&]++;
}
if(tmp[] < tmp[]) B |= (<<i);
}
// bug(1)
for(int i = ; i < n; i++) A[i] ^= B;
for(int i = ; i < n; i++) for(int j = i+; j < n; j++)
ans += (A[i] < A[j]);
return ans;
}
};
解题关键:发现异或后数的大小关系只和第k位有关。
div1
250pts
题意:字符串s(len <= 50),从中去掉K个字符之后,能否根据剩下的字符,确定去掉的是哪些字符呢。
分析:去掉K个字符后剩下的字符构成的序列如果不是原s中的唯一序列,那么就不能确定去掉的是哪K个字符,因此,可以枚举s中两个相等的字符s[i],s[j](i != j),然后
判断LCS(s[0, i-1], s[0, j-1]) + LCS(s[i+1, len-1], s[j+1, len-1]) + 1 >= len-K么,这样是能判断剩下的序列是否唯一。上面想得略复杂,题解只是判断两个相等字符之间的长度j-i<=K否,因为此时优先移走s[i],s[j]中的一个,然后将i,j之间的字符去掉,剩下的字符已经是相同的了,不论怎么取剩下的字符,最终一定不能确定被移走的是哪些字符。
代码:
const int maxn = ;
char sa[maxn], sb[maxn];
string str;
int dp[maxn][maxn]; class Decipherability {
public:
int len;
int LCS(int n, int m, bool rev) {
if(n == || m == ) return ;
if(rev) {
for(int i = n-; i >= ; i--)
sa[n--i] = str[i];
for(int i = m-; i >= ; i--)
sb[m--i] = str[i];
} else {
for(int i = ; i < n; i++)
sa[i] = str[len-n+i];
for(int i = ; i < m; i++)
sb[i] = str[len-m+i];
}
memset(dp, , sizeof dp);
for(int i = ; i <= n; i++)
for(int j = ; j <= m; j++)
if(sa[i-] == sb[j-]) {
dp[i][j] = dp[i-][j-] + ;
} else {
dp[i][j] = max(dp[i-][j], dp[i][j-]);
}
return dp[n][m];
}
string check(string s, int K) {
str = s;
len = sz(s);
if(len == K) return "Certain";
int ok = ;
for(int i = ; i < len; i++)
for(int j = i+; j < len; j++)if(s[i] == s[j]) {
if(LCS(i, j, true)+LCS(len-i-, len-j-, false) >= len-K-) {
ok = ;
break;
}
}
return ok ? "Certain" : "Uncertain";
}
};
解题关键:剩下的字符构成的序列在原字符串中不唯一。
550pts
题意:div2 500pts升级版,之前已经知道B[i],B[j]的大小只和第k位(x,1-x)及C的第k位y有关。
因此当y为0,需要知道A[j]第k位为1时,i < j 且A[i]的第k位为0 的个数;当y为1,A[j]第k位为0时,i < j,且A[i] 第k位为1的个数,注意A[i],A[j]的k+1位及更高位都应该相同,这样便能够分别统计C的第k位为0,1确定的(i, j)的个数(B[i] < B[j], i < j) 。
对A[i]排序后得到A'[l, r],从最高位开始,每次统计位于A'[l,r]范围的A[j],当A[j]的第k位为1或0时,位于A[j]之前且k位 为0或1的A[i](i < j)的个数,统计后根据第k位为0 或 1,分成A'[l, mid], A'[mid+1, r]两部分继续统计第k-1位能够确定的数对(i, j)的个数。划分成A'[l, mid], A'[mid+1, r]的目的是为了保证高位都相同,进而对下一位进行统计。具体可以用树状数组实现,为了免去每次都要对数组置零的耗时,可以对数组的每个元素设一个标记。
代码:
const int maxn = + ; int order[maxn], A[maxn];
LL ma[][];
LL res[maxn][];
int cnt; bool cmp(const int &x, const int &y) {
return A[x] < A[y];
} class XorSequence {
public: void add(int x, int v) {
while(x < maxn) {
if(res[x][] != cnt) {
res[x][] = ;
res[x][] = cnt;
}
res[x][] += v;
x += lowbit(x);
}
}
LL query(int x) {
LL ans = ;
while(x > ) {
if(res[x][] != cnt){
res[x][] = ;
res[x][] = cnt;
}
ans += res[x][];
x -= lowbit(x);
}
return ans;
}
void dfs(int l, int r, int dep) {
if(l > r || dep < ) return;
int mid = l-;
while(mid+ <= r) {
if(!(A[order[mid+]]&(<<dep)))
mid++;
else break;
} if(mid >= l && mid < r) {
cnt++;
//memset(res[dep], 0, sizeof(res[dep]));
for(int i = l; i <= mid; i++)
add(order[i], );
for(int i = mid+; i <= r; i++) {
ma[dep][] += query(order[i]);
}
// memset(res[dep], 0, sizeof(res[dep]));
cnt++;
for(int i = mid+; i <= r; i++)
add(order[i], );
for(int i = l; i <= mid; i++)
ma[dep][] += query(order[i]);
}
dfs(l, mid, dep-);
dfs(mid+, r, dep-);
}
long long getmax(int N, int sz, int A0, int A1, int P, int Q, int R) {
A[] = A0;
A[] = A1;
for (int i = ; i <= sz; i++) {
A[i] = (1LL*A[i - ] * P%N + 1LL*A[i - ] * Q%N + R) % N;
}
int n = __builtin_popcount(N-);
// for(int i = 1; i <= sz; i++)
// printf("%d ", A[i]);
// cout << endl; for(int i = ; i <= sz; i++)
order[i] = i;
sort(order + , order + sz + , cmp);
cnt = ;
memset(ma, , sizeof ma);
memset(res, , sizeof res); dfs(, sz, n-);
LL ans = ;
for(int i = ; i < n; i++)
ans += max(ma[i][], ma[i][]);
TL
return ans;
}
};
解题关键:排序后根据第k位对数组进行分组,统计。
850pts
题意:div2 500pt升级版,范围更大,且开始给定的n ( n <= 50)个序列,长度len及能够进行的split操作总次数<=1e9。
分析:官方题解是采用二分时间,然后在确定时间下单独考虑消除每个序列,二分需要的最少split次数,最后判断总的split次数是否<=splits。
难点在于怎么二分确定需要的最少split次数,题解里面给出了一系列证明,总结起来就是:先进行split操作,然后进行remove操作,这样结果肯定是更优的;
split的次数越多,结果更优。因此首先进行split操作,一个序列能够split成两个时,则split,如果满足split次数,能够继续将两个序列split成四个时,则split,继续split直到不能split所有序列,那么利用剩余的split次数对部分序列split,对没有split的其他部分序列进行remove操作,此时时间记做T1。剩余部分需要的时间T2和剩下的序列总长度,n'有关,即T2 = ,判断T1+T2 <= timeLimit。
代码:
class CartInSupermarket {
public:
int calcmin(vector <int> a, int b) {
int low = , high = (int)1e9;
while(low < high) {
int mid = (low+high)/;
if(possible(a, mid, b)) high = mid;
else low = mid+;
}
return low;
}
private:
bool possible(vector<int> a, int timeLimit, int b) {
LL sum = ;
for(int x: a) sum += numSplits(x, timeLimit);
return sum <= b;
}
int numSplits(int x, int timeLimit) {
int low = , high = x;
while(low < high) {
int mid = (low+high)/;
if(possible(x, mid, timeLimit)) high = mid;
else low = mid + ;
}
if(low == x) return (int)1e9 + ;
return low;
}
bool possible(LL x, LL numSplits, int timeLimit) {
LL numParts = ;
int tl = ;
while(numParts <= numSplits){
numSplits -= numParts;
numParts *= ;
tl++;
}
x = max(0LL, x-(numParts-numSplits));
return (tl + ) + (x + numParts + numSplits - ) / (numParts + numSplits) <= timeLimit;
}
};
解题关键:二分时间,并二分split次数,确定的splits次数下,最优化进行操作的过程。
TopCoder 649 div1 & div2的更多相关文章
- TopCoder 603 div1 & div2
div2 250pts MiddleCode 题意:s串长度为奇数时,将中间字符取掉并添加到t末尾:长度为偶数时,将中间两个较小的字符取掉并添加到末尾. 分析:直接做,学习了一下substr(s, p ...
- 【前行&赛时总结】◇第4站&赛时9◇ CF Round 513 Div1+Div2
◇第4站&赛时9◇ CF Round 513 Div1+Div2 第一次在CF里涨Rating QWQ 深感不易……作blog以记之 ( ̄▽ ̄)" +Codeforces 的门为你打 ...
- topcoder 649 DIV2
8 A:模拟 9:B:终于看懂题目... 题意:最多分解K次 每分钟一个数可以分解成两个数 或者-1: 关键字:DP,记忆花搜索. DP[I][J]=min(dp[i][j],1+max(dp[ii] ...
- Topcoder Srm 673 Div2 1000 BearPermutations2
\(>Topcoder \space Srm \space 673 \space Div2 \space 1000 \space BearPermutations2<\) 题目大意 : 对 ...
- Topcoder Srm 671 Div2 1000 BearDestroysDiv2
\(>Topcoder \space Srm \space 671 \space Div2 \space 1000 \space BearDestroysDiv2<\) 题目大意 : 有一 ...
- 求拓扑排序的数量,例题 topcoder srm 654 div2 500
周赛时遇到的一道比较有意思的题目: Problem Statement There are N rooms in Maki's new house. The rooms are number ...
- Topcoder srm 632 div2
脑洞太大,简单东西就是想复杂,活该一直DIV2; A:水,基本判断A[I]<=A[I-1],ANS++; B:不知道别人怎么做的,我的是100*N*N;没办法想的太多了,忘记是连续的数列 我们枚 ...
- TopCoder SRM500 Div1 250 其他
原文链接https://www.cnblogs.com/zhouzhendong/p/SRM500-250.html SRM500 Div1 250 题意 (看题用了半个小时--) 有 n 个人(编号 ...
- TopCoder SRM500 Div1 500 分治
原文链接https://www.cnblogs.com/zhouzhendong/p/SRM500-500.html SRM500 Div1 500 没想到 double 的精度居然没有爆-- 考虑以 ...
随机推荐
- Vivado HLS与System Generator:联系与区别
在很多年以前的ISE套件里面,有个功能强大的AccelDSP,它可以可自动地进行浮点到定点转换,并把算法生成可综合的HDL,还可以创建用于验证的测试平台,但是在4年前左右的时候销声匿迹了,当时的说法是 ...
- WPF中XAML转义字符
字符 转义字符 备注 & (ampersand) & 这个没什么特别的,几乎所有的地方都需要使用转义字符 > (greater-than character) > 在属性( ...
- JNI 学习笔记
JNI是Java Native Interface的缩写,JNI是一种机制,有了它就可以在java程序中调用其他native代码,或者使native代码调用java层的代码.也 就是说,有了JNI我们 ...
- TAG的用法和用途[转]
用一个例子来说明:一个combobox控件...一个textBox控件...一个datagridview控件!datagridview控件是连接数据库的...combobox和textBox是联合查询 ...
- android----sqlite中的 query() 参数分析
public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, Strin ...
- ORA-1653: unable to extend table SYS.AUD$
今早运维组的同事反映有个系统功能很多地方都报错,怀疑是不是数据库有什么问题.于是登录数据库检查,通过crsctl status res -t检查,发现所有集群资源都是OK的,没有哪个资源挂掉了.于是到 ...
- C++11右值引用,移动主义
理解1: 左值和右值针对等号而言, 等号左边称为左值, 等号右连称为右值. 理解2: 左值和右值针对表达式而言, 表达式结束后依然存在的持久对象称为左值, 表达式结束后不存在的持久对象称为右值. 理解 ...
- iOS 系统二维码扫描(可限制扫描区域)
使用 AVFoundation系统库来进行二维码扫描并且限制扫描二维码的范围.(因为默认的是全屏扫描) -(void)beginCode { //1.摄像头设备 AVCaptureDevice *de ...
- net windows Kafka
net windows Kafka 安装与使用入门(入门笔记) 完整解决方案请参考: Setting Up and Running Apache Kafka on Windows OS 在环境搭建 ...
- .Net webservice动态调用
直接贴代码吧 public class PmsService { /// <summary> /// pms接口 /// </summary> /// <param na ...