C. Removing Smallest Multiples
难度1200
题意:给定一个串S和一个串T S串是{1,2,......,n}的数 而T串是输入的 现在要使得S串变为T串的
最小花费 删除操作是删除k的最小倍数的数 花费的定义为k的总和
思路 直接用 i 枚举 1 - n 然后用这个数 i 去把1-n中 i 的所有连续倍数要删的数都删掉 这样的花费一定是最小的
细节 0表示要删的 1表示不用删的 2表示已经删了的
代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int vis[1000007];
bool solve()
{
int n;
cin >> n;
for (int i = 1;i <= n;i++)
{
char c;
cin >> c;
vis[i] = c == '1';
}
ll sum = 0;
for (int i = 1;i <= n;i++)
{
if (vis[i] == 1) continue;
for (int j = i;j <= n;j += i)
{
if (vis[j] == 1) break;
if (vis[j] == 0)
{
vis[j] = 2;
sum += i;
}
}
}
cout << sum << '\n';
return true;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
if (!solve()) cout << -1 << '\n';
}
return 0;
}
难度1400
题意:给定a b二进制串 然后每次操作都可以选择一个 l 和 r 然后a串[ l , r ]区间的数取反 b串的[1,l-1]和[ r+1,n ]区间取反 问a和b能不能同时变为00000... 输出操作数和每次的操作区间
思路:我们可以从末状态开始操作看看能操作到什么样的状态 可以发现 操作奇数次的时候 可以发现a b 的每一位都不相同 操作偶数次的时候 可以发现a b 的每一位都是相同的 那么有解的情况就一定是每一位都相同或者每一位都不同 那么我们只需要构造出一组操作步数小于等于n+5的 方案即为答案
构造方法(只判断一定有解的情况)
首先先把a全部变为0 这需要的操作步数最多是n 然后b只有两种状态 要么全0要么全1 如果是全0就不用操作了 如果是全1 那么就先操作[ 1,n ]先把a全部全为1 再操作[ 1,1 ] 将a1变为0 b[2,n]变为0 最后操作[ 2,n ] 将a[ 2,n ]操作为全0 b1操作为0 这时候就是全0了 操作步数最多是n+3 合法
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=2e5+10;
int a[N],b[N];
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
char c;
cin>>c;
a[i]=c-'0';
}
int cnt=0;
for(int i=1;i<=n;i++)
{
char c;
cin>>c;
b[i]=c-'0';
cnt+=(b[i]==a[i]);
}
if(cnt!=n&&cnt!=0)
{
cout<<"NO"<<endl;
return ;
}
bool fl;
if(cnt==n)
fl=0;
else fl=1;
vector<PII> ans;
for(int i=1;i<=n;i++)
{
if(a[i]==0) continue;
int j=i;
while(j+1<=n&&a[j+1]==1) j++;
ans.push_back({i,j});
fl^=1;
i=j;
}
if(fl)
{
ans.push_back({1,n});
ans.push_back({1,1});
ans.push_back({2,n});
}
cout<<"YES"<<endl;
cout<<ans.size()<<endl;
for(auto [x, y]:ans) cout<<x<<' '<<y<<endl;
}
int main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
难度1300
题意:给定一个串经过一些操作变为不减的串 可以证明一定有解
操作:选择一个l和r 如果 al + ar 为奇数 让ar = al 如果是偶数 让al=ar
思路: 虽然有很多种可能性 但是是不是有一种可能可以使得所有元素都变成相同元素从而构造一个合法解呢?
答案是肯定可以的 然后应该把整个序列变成哪个元素呢?那么是不是应该去看看是不是首尾元素的其中一个呢?因为操作是把某个元素变为前面一个元素或者后面一个元素 那么简单一点想 肯定是首尾元素的 那么我们用a[1]代表首元素 a[n]代表尾元素
构造方法:
1.如果a[1]是偶数 a[n]也为偶数 由于偶数加偶数一定等于偶数并且相加如果是偶数 就让al=ar也就是前面的等于后面的 那么就可以把 1- (n-1)中的所有偶数都变为a[n] 然后a[1]=a[n]也是偶数并且相加为奇数 让ar = al 也就是后面等于前面 那么就可以把 2 - (n-1)的所有奇数都可以变为a[n]了
2.如果a[1]为奇数 a[n]为奇数 由于奇数加奇数一定为偶数 并且如果是奇数 就让al=ar 那么就是让1-(n-1)中的所有奇数都变为a[n]了 然后这时候a[1]=a[n]为一个奇数 然后奇数加偶数为奇数 如果 相加为奇数 让ar = al 那么就可以让 2-(n-1)的所有数变为a[1]=a[n]
3.如果a[1]为奇数 a[n]为偶数 由于奇数加偶数为奇数 那么就先让a[n]=a[1] 然后两个都为奇数了 就转换为第二种情况了
4.如果a[1]为偶数 a[n]为奇数 由于奇数加偶数为奇数 那么就让 a[n]=a[1]就转换为第一种情况了
这时候所有情况都枚举完了 就可以做了
由于不限制怎么交换 交换多少次 所以怎么样都ok的 就算重复操作也没问题
实现代码 (我这里样例输出都是重复的 如果不想用可以用上面思路再写一份)
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
vector<pair<int,int>> ans;
if(a[1]%2==0&&a[n]%2==0)
{
for(int i=1;i<=n-1;i++)
if(a[i]%2==0)
ans.push_back({i,n});
else
ans.push_back({1,i});
}
else if(a[1]%2==1&&a[n]%2==1)
{
for(int i=1;i<=n-1;i++)
if(a[i]%2==1)
ans.push_back({i,n});
else
ans.push_back({1,i});
}
else if(a[1]%2==1&&a[n]%2==0)
{
ans.push_back({1,n});
for(int i=1;i<=n-1;i++)
if(a[i]%2==1)
ans.push_back({i,n});
else
ans.push_back({1,i});
}
else
{
ans.push_back({1,n});
for(int i=1;i<=n-1;i++)
if(a[i]%2==0)
ans.push_back({i,n});
else
ans.push_back({1,i});
}
cout<<ans.size()<<endl;
if(ans.size())
{
for(int i=0;i<ans.size();i++)
cout<<ans[i].first<<" "<<ans[i].second<<endl;
}
}
int main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
难度1500
题意:博弈游戏 给定一个序列 然后每一个回合都可以删一个数 删到没有数时游戏结束 然后如果Alice删的数总和为偶数的话Alice赢 否则bob赢 Ailce先手
思路:这里是看了某位贴吧大佬的题解
首先我们可以发现很重要的一点就是:偶数不会影响Alice手中的数的奇偶性的改变。一个奇数可以改变一次,我们可以按照这个结论去找突破口。
很多人的第一感觉是看奇数的奇偶来找结论,我们先随便试一试几组样例:
我们首先去找一下答案和奇数的什么有关。我们先看样例,1 3 5 7显然这时候Alice一定能拿到两个奇数。但是如果是1 3,这时候Alice就只能拿到一个,所以就输了。假如存在1 3 5 8,这时候Alice必胜,但是1 2 4 6 8的话,Alice就会输掉。
我们可以感觉到,只获得奇数数量的奇偶性是不够的,我们发现% 4 = 0 / 1 / 2 / 3 答案都有可能不同。
我们发现,每多4个奇数或者4个偶数,是不会影响结果的。因此,影响结果的数是刚开始的n和奇数数量 % 4。对于一个人来说 看是否是偶数的话就是%2 但是现在是两个人 那么就会乘2 变为%4
我是这样理解的
分类四种情况讨论:
奇数 % 4 == 0 :
先手必胜,因为不管怎么拿先手都能拿到两两一组的奇数使得相加变成偶数。
奇数 % 4 == 1 :
如果n是偶数,那么先手必胜,否则先手必败。 简单来说,我们可以把这个情况看成1 2 4 8和1 2 4 6 8这两个样例。1248是先手必胜,因为Alice只要不拿到那个奇数,并且他是第一个拿的,所以一定可以把这个奇数留给Bob。 12468的话刚好多了一个奇数,因此两人都不想拿这个奇数,所以最后一定剩给Alice。
奇数 % 4 == 2 :
先手必败,因为先手必然会拿到一个奇数,不能和另一个奇数配对。
奇数 % 4 == 3 :
先手必胜,因为先手必然会可以在三个奇数中拿到2个,直接获得胜利。
代码
#include <bits/stdc++.h>
#define NO cout<<"Bob"<<endl
#define YES cout<<"Alice"<<endl
using namespace std;
const int N=1e5+10;
int a[N];
int n;
void solve()
{
cin >> n;
int zero = 0, one = 0;
for(int i = 1 ; i <= n ; i ++ ) cin >> a[i], zero += (!(a[i] & 1)), one += (a[i] & 1);
if(!one) {YES;}
else if(one & 1)
{
if(one % 4 == 1) {
if(n & 1) {NO;}
else {YES;}
}
else if(one % 4 == 3) {YES;}
}
else{
if(one % 4 == 0) {YES;}
else {NO;}
}
}
int main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
本题还有dp解法 记忆化搜
int dfs(int x, int y, int cur, int turn) {
int &v = f[x][y][cur][turn];
if(~v) return v;
if(!x && !y) return !cur ^ turn;
int res = 0;
if(x) res |= !dfs(x - 1, y, cur, !turn);
if(y) res |= !dfs(x, y - 1, cur ^ !turn, !turn);
return v = res;
}