voidsolve(){ int t; cin >> t; while (t--) { int n, k; cin >> n >> k; string s; cin >> s; int num[k]={0}; for (int i = 0; i < s.size(); i++) if (s[i] == '1') num[(i + 1) % k] += 1; int flag = 0; for (int i = 0; i < k; i++) { if (num[i] % 2 != 0) { cout << "NO" << '\n'; flag = 1; break; } } if (!flag) { cout << "YES" << '\n'; } } }
voidsolve(){ int t; cin >> t; while (t--) { int n, k; cin >> n >> k; int cnt[n + 1] = {0}, dp[n + 1] = {0}; int tem = 0; vector<int> val; for (int i = 0; i < n; i++) { int x; cin >> x; cnt[x]++; } for (int i = 0; i <= n; i++) if (cnt[i] != 0) val.push_back(i); sort(val.begin(), val.end()); int s = val.size(); //如果上一轮拿走一个是奇数,必赢 if (cnt[val[s - 1]] % 2 == 0) dp[s - 1] = 1; for (int i = s - 2; i >= 0; i--) { //当前剩余:cnt[val[i]]-1个数 if (cnt[val[i]] % 2 != 0) { //如果当前按方式2继续拿下去的话,当前阶段是必输的。所以只能进行跳跃 for (int j = i + 1; j < s; j++) { //去寻找存不存在一个j使得dp[j]=0,代表当前玩家在这一轮只要取val[j]就能必赢 if (val[j] - val[i] <= k) { if (dp[j] == 0) { //如果存在,当前玩家就跳到这个必赢的那个阶段 dp[i] = 1; break; } } } } else dp[i] = 1; } int flag = 0; // cout<<dp[1]<<endl; for (int i = 0; i < s; i++) if (dp[i]) flag = 1; if (flag) cout << "YES"; else cout << "NO"; cout << '\n'; } }
TLE的原因是因为第二层循环的暴力枚举:
1 2 3 4 5 6 7 8
for (int j = i + 1; j < s; j++) { if (val[j] - val[i] <= k) { if (dp[j] == 0) { dp[i] = 1; break; } } }
voidsolve(){ int t; cin >> t; while (t--) { int n, k; cin >> n >> k; int cnt[n + 1] = {0}, dp[n + 1] = {0}; int tem = 0; vector<int> val; set<int> lose; for (int i = 0; i < n; i++) { int x; cin >> x; cnt[x]++; }
for (int i = 0; i <= n; i++) if (cnt[i] != 0) val.push_back(i);
sort(val.begin(), val.end());
int s = val.size(); //如果上一轮拿走一个是奇数,必赢
if (cnt[val[s - 1]] % 2 == 0) dp[s - 1] = 1; if (!dp[s - 1]) lose.insert(s - 1); for (int i = s - 2; i >= 0; i--) { int flag=1; //当前剩余:cnt[val[i]]-1个数 if (cnt[val[i]] % 2 != 0) {
//合法的跳跃约区间 O(log(n)) int pos = upper_bound(val.begin(), val.end(), val[i] + k) - val.begin()-1 ; //在lose里面找有没有大于i下标(大于i下标就代表是之前记录好的状态)的失败状态 //lose.upper_bound(i)没找到返回lose.end() auto it = lose.upper_bound(i); if(it!=lose.end()&&(*it)<=pos){ dp[i]=1; flag=0; }
} else dp[i] = 1; if(flag) lose.insert(i); } int flag = 0; // cout<<dp[1]<<endl; for (int i = 0; i < s; i++) if (dp[i]) flag = 1; if (flag) cout << "YES"; else cout << "NO"; cout << '\n'; } }
E. Friendly Gifts
题目分析
如果一个数组是good数组,那么数组要满足
没有重复,2. 最大值 - 最小值 + 1 = 长度
题目要找两个长度一样的 good 子段,而且它们合起来也要 good。比如长度是 4。第一段如果是:[1,2,3,4]。第二段就必须刚好接上:[5,6,7,8].这样合起来才是:[1,2,3,4,5,6,7,8]。所以核心为找一段数值范围是 [x, x+len-1],再找一段数值范围是 [x+len, x+2len-1]