做题小结 2.07-2月底
https://codeforces.com/contest/1891/problem/C

这个题就是一个贪心双指针的题目 排完序一直累计数你ar可以被杀就用
思路很简单 但是细节很多 比如处理l r 如果最终还剩一个 那必然是打一半然后必杀一半(还要加上此时连击) 如果剩下两个数
就是l+1==r了 此时这种情况其实就是al为0了(在我的代码是这样的)此时也只需要考虑ar即可 总体来说此题细节很多 你还要考虑ar会不会是1 是1直接一次就行 而不是两次 。。
void solve(int t) {
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
sort(a + 1, a + 1 + n);
int r = n;
int l = 0;
int now = 0;
int ans = 0;
while (l < r) {
now += a[l];
if (now >= a[r]) {
ans += a[r] + 1;
a[l] = now - a[r];
r--;
//l==r
now = 0;
if (l == r) {
int w = a[l];
if (w != 0) {
if (w % 2 == 0) {
w = w / 2;
ans += w + 1;
} else {
ans++;
if (w != 1) {
w = (w - 1) / 2;
ans += w + 1;
}
}
}
// debug
cout << ans << endl;
return ;
}
} else if (l + 1 < r) {
l++;
} else {
break;
}
}
if (now ) {
int w;
w = (a[r] - now);
if (w != 0) {
if (w % 2 == 0) {
w = w / 2;
ans += now + w + 1;
} else {
ans++;
w = (w - 1) / 2;
ans += now + w + 1;
}
}
cout << ans << endl;
return ;
}
//now==0 al=0
if (l + 1 == r) {
int w = a[r];
if (w != 0) {
if (w % 2 == 0) {
w = w / 2;
ans += now + w + 1;
} else {
ans++;
int temp = w;
w = (w - 1) / 2;
ans += now + w + 1 - (temp == 1);
}
}
cout << ans << endl;
return ;
}
}
}
https://codeforces.com/contest/1888/problem/C

是一个道阅读理解 拿到手可能感觉很棘手 但实际上 好好读完题 作为子序列恰好出现一次
意味什么呢 你选中的这个子序列中一定至少有一个数字他是最后一次出现了
至此 这个题就出来了 枚举每一个位置 加和即可
https://codeforces.com/contest/1884/problem/B

这是一个很好的题目
我来讲下思路 我们如果碰到某一位不能被整除了 那必然需要把后面的1全移上来 发现这个状态是可以前后转移的 答案可以累加 我们每次找到一个0然后就可以完成操作了 下次让指针++移到下一位去
会发现这个i给的很好 因为他是从1开始的 我们这里认为指针与i相等时认为是这样的
相当于 此时是2i-1与2指针相除 所以需要向前一位
//不错的题
cin>>n>>s;
for(int i=n-1;i>=0;i--)
{
a[n-i]=s[i]-'0';
}
int pos=1;
int ans=0;
for(int i=1;i<=n;i++)
{
while(a[pos])pos++;
if(pos>n){
cout<<-1<<" ";
continue;
}
//理论上pos要大于i
ans+=pos-i;
cout<<ans<<" ";
pos++;
}
https://codeforces.com/contest/1884/problem/C

非常极端的贪心
只猜到最终我们计算1和k的值即可 认为这个就是最小值 然后别的只要一直计算即可
还是很难想到 第二次再做的时候
struct node{
int l,r;
}seg[range];
//int seg[range];
void solve(int t ) { //多测
cin >> n>>k ;
// for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)
{
cin>>seg[i].l>>seg[i].r;
}
map<int,int>ma;
for(int i=1;i<=n;i++)
{
if(seg[i].l==1)continue;
ma[seg[i].l]++;
ma[seg[i].r+1]--;
}
int last=0;
int dep=0;
int ans=0;
for(auto &i:ma)
{
dep+=i.second;
// last+=i.second;
ans=max(dep,ans);
}
ma.clear();
for(int i=1;i<=n;i++)
{
if(seg[i].r==k)continue;
ma[seg[i].l]++;
ma[seg[i].r+1]--;
}
last=0;
dep=0;
for(auto &i:ma)
{
dep+=i.second;
ans=max(dep,ans);
// last+=i.second;
}
cout<<ans<<endl;
https://codeforces.com/contest/1882/problem/C

第二次做 把这题和另外一个题搞反了 然后讲下思路吧 这个题
可以把第三张牌含第三的正数都拿了 于是就剩两张了
为什么可以 因为第一遍全拿奇数 第二遍 我们删除一个1或者2 即可了 然后讨论下12的正负数关系即可
https://codeforces.com/contest/1869/problem/B

这个题当时想了蛮久的 没做出来 这次很快就想出来
很显然只需要枚举初始点和终点最近的主要城市 然后分类讨论st fin是否是主要城市即可
https://codeforces.com/contest/1867/problem/C

诈骗题 注意审题 n个不同的整数 也就是说 bob如果去删mex-1以内的数 那么你就要去补了
所以bob那肯定会去删除mex-1以内的数所以理论上你只能增加一次mex 所以代码就出来了
cout<<mex;
while(1)
{
int x;
cin>>x;
if(x==-1||x==-2){
cout<<endl;
return ;
}
cout<<x;
cout<<endl;
cout.flush();
}
https://codeforces.com/contest/1869/problem/C

n>=m时候 答案最多就是m 多的那几行全填一样的就行 然后对角线去填 默认最后一列mex=0 次mex=1 这样就搞出来了。。反正就是猜 对角线去填 然后搞清楚每一列的作用就行
有点忘了思路了 反正切掉了 记得就是自己去认为让每一个列去少某一个值 然后填数字对角线去填
https://codeforces.com/contest/1858/problem/B

这个题第二次做 还是没想到题解思路 自己只想到要找到那种差是大于d中间吃一次然后到点又吃一次的那种要删除 可能也跟思考时间有关 不过我觉得给我再一些时间还是没想到正解的
题解的思路很好 属于站在那种更高层次去做这个题
先求一遍总的 然后去探讨枚举删除某个点的影响 把它拆开

很好的思路 上取整默认Sx点吃一个 到点就吃 Sx+1留着自己作为左端点再吃
https://codeforces.com/contest/1859/problem/C

第二次做还是很快想出来了
int w=(i-1)(i)(2(i-1)+1)/6;这个公式要知道 求前i-1平方和
摸一个10样例发现 710 89 ...107这样最好 于是我们发现可能要互换直接枚举 这个很简单不贴代码了
https://codeforces.com/contest/1859/problem/B

这道题容易漏思考 其实就是我刚前面题解想说的容易漏情况
我一开始以为只要都放最小去就好了 实际上有没有可能我最小很小但我次小蛮大的 那不就亏了?
所以我们认为先全部取次小然后减最小和一个最小的次小那就是答案了
https://codeforces.com/contest/1856/problem/C


很好的一道题 注意到如果靠模拟去倒着写很明显 我们很难知道最终最大值是多少 因为我们有着k的限制 我第二次做(思考了一小会)我以为固定最终答案然后求的这个k判断题中给的k诈骗什么什么的这个思路很垃圾完全是依托 正确做法还得是二分mid 然后怎么确定我们可行的
很可惜 我还是没观察到i-j的关系 我们想要操作到maxn 我们就要知道这一段序列从哪里开始哪里结束
哪里结束很重要 开始完全可以on 结束也可以on 数据支持评平方 但是哪里结束就是个问题关系到k的可行
于是判断哪里结束就要观察到结束的数字最少是多少才能满足我两两+1进行递进使得达到mid
比如 2 4 5 我要变7假设从2 开始那么我至少要多哪里结束 很明显是4因为它可以变成6 那如果4改3呢行不行假设k无穷也是行的 那么看来结束完全看下一个数能否支持 如果5小了肯定不行的 所以判断怎么判断呢
很明显2 4 4 变7就不行 那么我们结束后那个值最小也应是我们mid-递进的次数 所以可以得到mid-递进次数 那么递进次数是多少呢 很明显其实是j-i因为7->6->5传递两次 所以是mid+i-j
求得结束就要求每个数变成最少操作的数的代价 于是我们完全可以求得比如2 4 5 中4要变6理论上4要是5就好了那递进一次就ok了 所以代价是理论的最小值(其实 这个数没达到)-这个数
最终判断总和是否小于等于k即可
bool check(int mid) {
//枚举每个数进行 只看它身后数字
for (int i = 1; i < n; i++) {
// int last = i + 1;
int last=0;
for (int j = i + 1; j <= n; j++) {
if (a[j] >= mid + i - j) {
last = j;
break;
} //6 5 4 3 最小操作数 比如a5=3 可以操作那么a4一定小于等于3
//6 5 4 1 5
// j=5 此时 7-5+1<=5 只要此时是3都ok的 这里可以保证
//我们进行操作 j=5之前一定有比我小的
}
int sum = 0;
// if(last==i+1)break;
if(last==0)break;
for (int j = i; j <= last - 1; j++) {
sum += (mid + i - j) - a[j];
}
if (sum <= k) {
// cout<<mid<<" "<<i<<" "<<last<<endl;
return 1;
}
}
return 0;
}

浙公网安备 33010602011771号