前言
上大分的一场!加油!!
一、A. Table with Numbers
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
int n,h,l;
cin>>n>>h>>l;
vector<int>a(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
if(h>l)
{
swap(h,l);
}
int no=0;
int row=0;
int both=0;
for(int i=1;i<=n;i++)
{
if(a[i]>l)
{
no++;
}
else if(a[i]>h)
{
row++;
}
else
{
both++;
}
}
if(row<=both)
{
cout<<row+(both-row)/2<<endl;
}
else
{
cout<<both<<endl;
}
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
首先,还是先考虑让更大的做宽。那么之后可以发现,对于数组里的数,可以被分为不能被选的 no、只能被选来做宽的 row 和做宽做长都可以的 both。那么在统计出这三种的个数后,若只能用来做宽的个数小于等于两者均可以的,那么肯定是首先选择只能做宽的和都可以的配对,产生 row 点贡献,然后再从剩下的 both 里两两一对产生贡献。否则,即只能用来做宽的大于均可以的,那么就只能让产生 both 点贡献。
二、B. The Curse of the Frog
byd上来题就读了一会,又被贪心策略卡了一会,闹麻了......
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
ll n,x;
cin>>n>>x;
vector<array<ll,3>>jump(n+1);
for(int i=1;i<=n;i++)
{
//a b c
cin>>jump[i][0]>>jump[i][1]>>jump[i][2];
}
ll cur=0;
for(int i=1;i<=n;i++)
{
cur+=(jump[i][1]-1)*jump[i][0];
}
if(cur>=x)
{
cout<<0<<endl;
return ;
}
vector<array<ll,3>>b;
for(int i=1;i<=n;i++)
{
if(jump[i][0]*jump[i][1]>jump[i][2])
{
b.push_back(jump[i]);
}
}
if(b.empty())
{
cout<<-1<<endl;
return ;
}
sort(b.begin(),b.end(),[&](auto &x,auto &y)
{
return x[0]*x[1]-x[2]>y[0]*y[1]-y[2];
});
ll step=b[0][0]*b[0][1]-b[0][2];
ll rest=x-cur;
cout<<(rest+step-1)/step<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
首先,肯定是可以考虑白嫖每种跳跃的距离,即用每种类型都跳 b[i]-1 次,使得不发生回滚。此时,若已经大于等于终点了,那么直接返回0即可。否则,因为之后再跳就发生回滚了,那么净收益小于等于0的所有类型肯定都不能再用了,因为必然不会导致距离前进。那么若此时不存在收益大于0的跳跃,就不可能达到。之后,贪心地想,肯定是只用收益最大的跳跃,才能使得回滚次数最少,那么就按净收益排序,选最大的计算即可。
三、C1. XOR Convenience (Easy Version)
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
int n;
cin>>n;
set<int>st;
for(int i=2;i<=n;i++)
{
st.insert(i);
}
vector<int>ans(n+1);
ans[n]=1;
for(int i=2;i<=n-1;i++)
{
ans[i]=ans[n]^i;
st.erase(ans[i]);
}
ans[1]=*st.begin();
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<" ";
}
cout<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
C1就是只需要考虑 i 的范围不包括1位置时的构造方法。因为唯一没有限制可以随便填的就是最后一个位置,且每个位置都和后缀位置的数有关,所以优先考虑从最后位置开始填。那么一个自然的想法就是可以让前面的每个数字的 j 都等于最后位置,那么其实可以考虑给最后位置填1,这样前面每个位置异或1都是两两一对的了。最后,只需要把没出现的补到1位置即可。
四、C2. XOR-convenience (Hard Version)
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void brute()
{
int n=12;
vector<int>p(n+1);
for(int i=1;i<=n;i++)
{
p[i]=i;
}
vector<vector<int>>ans;
do
{
bool br=false;
for(int i=n-1;i>=1;i--)
{
bool flag=false;
for(int j=i;j<=n;j++)
{
if(p[i]==(p[j]^i))
{
flag=true;
break;
}
}
if(!flag)
{
br=true;
break;
}
}
if(!br)
{
ans.push_back(p);
}
}while(next_permutation(p.begin()+1,p.end()));
if(ans.size())
{
cout<<ans.size()<<endl;
for(auto &vec:ans)
{
if(vec[n]!=1)
{
continue;
}
for(int i=1;i<=n;i++)
{
cout<<vec[i]<<" ";
}
cout<<endl;
}
}
else
{
cout<<-1<<endl;
}
}
void solve()
{
int n;
cin>>n;
int d=0;
while((1ll<<d)<n)
{
d++;
}
if((1ll<<d)==n)
{
cout<<-1<<endl;
return ;
}
set<int>st;
for(int i=2;i<=n;i++)
{
st.insert(i);
}
vector<int>ans(n+1);
ans[n]=1;
for(int i=2;i<=n-1;i++)
{
ans[i]=ans[n]^i;
st.erase(ans[i]);
}
ans[1]=*st.begin();
if(n%2==0)
{
int lowbit=n&-n;
swap(ans[1],ans[lowbit]);
}
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<" ";
}
cout<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
C2就比较困难了。
若加上1位置的限制,首先注意到奇数长度时,因为每个位置上的数都是异或最后一位的1得来的,而 n 这个数因为是奇数所以第0位必然是1。所以,在之前构造的过程中,n 这个数一定会被填过。那么不管1位置填什么,异或1后+1或者-1的结果都是小于等于n的,那么就维持不变就可以了。
而当是偶数长度时,因为第0位是0,那么在前面构造的过程中必然不可能存在一个数异或上1的结果是 n。那么,第1个位置补的时候就必然会补成 n,那么后续就必然不存在一个数使得其异或1的结果是 n。此时就可以考虑把1位置的数和某个位置交换一下,从而使得整个构造合法。
回顾不合法的原因,是因为偶数不存在第0位的1,异或1必然导致+1,所以后续不存在一个数异或1的结果是 n。那么其实可以考虑去找一个是1的位,让其异或这个位后把这位上的1消去即可。所以,就可以去找 n 的 lowbit,然后交换1位置和 lowbit 位置的数。首先,容易证明交换到1位置的数在后续必然是存在的,那么就只需要证明把 n 交换到 lowbit 位置是合法的。因为要求后续存在一个位置 j 上的数 x,使得这个数等于 n 异或 lowbit。而又因为这个 x 是由 j 异或最后的1得到的,那么 n 在去掉 lowbit 后的这个数必然大于 lowbit,所以不管异或是+1还是-1,位置 j 必然在位置 lowbit 之后,所以这个方法是可行的。
五、D1. Little String (Easy Version)
太难了......
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
template<class T>
constexpr T power(T a, ll b) {
T res = 1;
for (; b != 0; b /= 2, a *= a) {
if (b & 1) {
res *= a;
}
}
return res;
}
template<int M>
struct ModInt {
public:
constexpr ModInt() : x(0) {}
template<typename T>
constexpr ModInt(T x_) {
T v = x_ % M;
if (v < 0) {
v += M;
}
x = v;
}
constexpr int val() const {
return x;
}
constexpr ModInt operator-() const {
ModInt res;
res.x = (x == 0 ? 0 : M - x);
return res;
}
constexpr ModInt inv() const {
return power(*this, M - 2);
}
constexpr ModInt &operator*=(const ModInt &rhs) &{
x = ll(x) * rhs.val() % M;
return *this;
}
constexpr ModInt &operator+=(const ModInt &rhs) &{
x += rhs.val();
if (x >= M) {
x -= M;
}
return *this;
}
constexpr ModInt &operator-=(const ModInt &rhs) &{
x -= rhs.val();
if (x < 0) {
x += M;
}
return *this;
}
constexpr ModInt &operator/=(const ModInt &rhs) &{
return *this *= rhs.inv();
}
friend constexpr ModInt operator*(ModInt lhs, const ModInt &rhs) {
lhs *= rhs;
return lhs;
}
friend constexpr ModInt operator+(ModInt lhs, const ModInt &rhs) {
lhs += rhs;
return lhs;
}
friend constexpr ModInt operator-(ModInt lhs, const ModInt &rhs) {
lhs -= rhs;
return lhs;
}
friend constexpr ModInt operator/(ModInt lhs, const ModInt &rhs) {
lhs /= rhs;
return lhs;
}
friend constexpr bool operator==(ModInt lhs, const ModInt &rhs) {
return lhs.val() == rhs.val();
}
friend constexpr bool operator<(ModInt lhs, const ModInt &rhs) {
return lhs.val() < rhs.val();
}
friend constexpr bool operator>(ModInt lhs, const ModInt &rhs) {
return lhs.val() > rhs.val();
}
friend constexpr bool operator<=(ModInt lhs, const ModInt &rhs) {
return lhs.val() <= rhs.val();
}
friend constexpr bool operator>=(ModInt lhs, const ModInt &rhs) {
return lhs.val() >= rhs.val();
}
friend constexpr bool operator!=(ModInt lhs, const ModInt &rhs) {
return lhs.val() != rhs.val();
}
friend constexpr std::istream &operator>>(std::istream &is, ModInt &a) {
ll i;
is >> i;
a = i;
return is;
}
friend constexpr std::ostream &operator<<(std::ostream &os, const ModInt &a) {
return os << a.val();
}
private:
int x;
};
template<int M, typename T = ModInt<M>>
struct Comb {
vector<T> fac;
vector<T> inv;
Comb(int n) {
fac.assign(n, 1);
for(int i=1;i<n;i++)
{
fac[i]=fac[i-1]*i;
}
inv.assign(n, 1);
inv[n-1]=fac[n-1].inv();
for(int i=n-2;i>=0;i--)
{
inv[i]=inv[i+1]*(i+1);
}
}
template<std::signed_integral U>
T P(U n, U m) {
if(n<m)
{
return 0;
}
return fac[n] * inv[n - m];
}
template<std::signed_integral U>
T C(U n, U m) {
if(n<m||m<0)
{
return 0;
}
return fac[n] * inv[n - m] * inv[m];
}
};
constexpr int M = 1e9+7;
using Z = ModInt<M>;
void solve()
{
ll n,c;
cin>>n>>c;
string s;
cin>>s;
s=" "+s;
if(s[1]=='0'||s[n]=='0')
{
cout<<-1<<endl;
return ;
}
Z ans=1;
for(ll i=1;i<=n-1;i++)
{
if(s[i]=='0')
{
c/=__gcd(c,i-1);
ans*=(i-1);
}
else
{
c/=__gcd(c,2ll);
ans*=2;
}
}
if(c==1)
{
cout<<-1<<endl;
}
else
{
cout<<ans<<endl;
}
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
首先,可以考虑哪些排列 p 满足字符串 w 的限制。那么可以发现,若 w[1]=0 或 w[n]=0 ,那么必然不可能存在合法的排列。因为只需要区间是0这个数字本身、区间是排列整体就可以使 MEX 不符合条件。所以可以考虑假设 w[1]=w[n]=1, 然后从0开始,往排列里插入数字。
对于当前来到的数字 k,此时 0~k-1 的数字已经排好了,那么就有 k+1 种插入方式。首先,考虑插入到开头或结尾的情况。那么就必然可以通过选择剩下的所有数字,使得 MEX 等于 k,即使之后再往中间插入的话也不会影响 MEX 了。之后,考虑插入到中间的情况。此时,因为 k 的左右两侧就都是小于 k 的数,又因为如果想让 MEX 等于 k 的话,就必须把所有小于 k 的数都包含,那么就必然会导致把 k 选入,所以此时的 MEX 是不可能等于 k 的。所以,若 w[i]=1,那么就有放头尾的两种选择;若 w[i]=0,那么就有 i-1 种选择。所以就有公式:
因为在D1中 w 就是 s 本身,所以只需要检查 f(s) 是否能被 c 整除即可。那么就只需要遍历所有的 i,然后让 c 除以和当前乘数的 gcd。若最终 c=1,那么就说明无答案,否则就输出即可。
六、D2. Little String (Hard Version)
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
template<class T>
constexpr T power(T a, ll b) {
T res = 1;
for (; b != 0; b /= 2, a *= a) {
if (b & 1) {
res *= a;
}
}
return res;
}
template<int M>
struct ModInt {
public:
constexpr ModInt() : x(0) {}
template<typename T>
constexpr ModInt(T x_) {
T v = x_ % M;
if (v < 0) {
v += M;
}
x = v;
}
constexpr int val() const {
return x;
}
constexpr ModInt operator-() const {
ModInt res;
res.x = (x == 0 ? 0 : M - x);
return res;
}
constexpr ModInt inv() const {
return power(*this, M - 2);
}
constexpr ModInt &operator*=(const ModInt &rhs) &{
x = ll(x) * rhs.val() % M;
return *this;
}
constexpr ModInt &operator+=(const ModInt &rhs) &{
x += rhs.val();
if (x >= M) {
x -= M;
}
return *this;
}
constexpr ModInt &operator-=(const ModInt &rhs) &{
x -= rhs.val();
if (x < 0) {
x += M;
}
return *this;
}
constexpr ModInt &operator/=(const ModInt &rhs) &{
return *this *= rhs.inv();
}
friend constexpr ModInt operator*(ModInt lhs, const ModInt &rhs) {
lhs *= rhs;
return lhs;
}
friend constexpr ModInt operator+(ModInt lhs, const ModInt &rhs) {
lhs += rhs;
return lhs;
}
friend constexpr ModInt operator-(ModInt lhs, const ModInt &rhs) {
lhs -= rhs;
return lhs;
}
friend constexpr ModInt operator/(ModInt lhs, const ModInt &rhs) {
lhs /= rhs;
return lhs;
}
friend constexpr bool operator==(ModInt lhs, const ModInt &rhs) {
return lhs.val() == rhs.val();
}
friend constexpr bool operator<(ModInt lhs, const ModInt &rhs) {
return lhs.val() < rhs.val();
}
friend constexpr bool operator>(ModInt lhs, const ModInt &rhs) {
return lhs.val() > rhs.val();
}
friend constexpr bool operator<=(ModInt lhs, const ModInt &rhs) {
return lhs.val() <= rhs.val();
}
friend constexpr bool operator>=(ModInt lhs, const ModInt &rhs) {
return lhs.val() >= rhs.val();
}
friend constexpr bool operator!=(ModInt lhs, const ModInt &rhs) {
return lhs.val() != rhs.val();
}
friend constexpr std::istream &operator>>(std::istream &is, ModInt &a) {
ll i;
is >> i;
a = i;
return is;
}
friend constexpr std::ostream &operator<<(std::ostream &os, const ModInt &a) {
return os << a.val();
}
private:
int x;
};
template<int M, typename T = ModInt<M>>
struct Comb {
vector<T> fac;
vector<T> inv;
Comb(int n) {
fac.assign(n, 1);
for(int i=1;i<n;i++)
{
fac[i]=fac[i-1]*i;
}
inv.assign(n, 1);
inv[n-1]=fac[n-1].inv();
for(int i=n-2;i>=0;i--)
{
inv[i]=inv[i+1]*(i+1);
}
}
template<std::signed_integral U>
T P(U n, U m) {
if(n<m)
{
return 0;
}
return fac[n] * inv[n - m];
}
template<std::signed_integral U>
T C(U n, U m) {
if(n<m||m<0)
{
return 0;
}
return fac[n] * inv[n - m] * inv[m];
}
};
constexpr int M = 1e9+7;
using Z = ModInt<M>;
void solve()
{
ll n,c;
cin>>n>>c;
string s;
cin>>s;
s=" "+s;
if(s[2]=='?')
{
s[2]='0';
}
if(s[1]=='0'||s[n]=='0')
{
cout<<-1<<endl;
return ;
}
s[1]='1',s[n]='1';
vector<int>odd;
int cnt=0;
for(ll i=1;i<=n-1;i++)
{
if(s[i]=='0')
{
c/=__gcd(c,i-1);
}
else if(s[i]=='1')
{
c/=__gcd(c,2ll);
}
else
{
cnt++;
if((i-1)%2==1)
{
odd.push_back(i);
}
}
}
//不是2的幂
if(c!=(1ll<<__lg(c)))
{
Z ans=1;
for(int i=1;i<=n-1;i++)
{
if(s[i]=='0')
{
ans*=(i-1);
}
else
{
ans*=2;
}
}
cout<<ans<<endl;
return ;
}
//是2的幂
//剩下的问号个数
int rest=cnt;
int d=__lg(c);
for(auto x:odd)
{
//剩下的全选2都不会超
if(rest<d)
{
break;
}
//不选2
s[x]='0';
rest--;
}
//偶数的个数大于等于d -> 无解
if(rest>=d)
{
cout<<-1<<endl;
return ;
}
Z ans=1;
for(int i=1;i<=n-1;i++)
{
if(s[i]=='0')
{
ans*=(i-1);
}
else
{
ans*=2;
}
}
cout<<ans<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
因为对于 s 中0或1的部分的贡献已经确定,这部分记为 b。而对于 s 中因为是?而没确定的部分,这部分记为 x。那么问题就变成了找到最小的 x,使得 bx 不能被 c 整除,bx 可以表示为:
因为 b 已经固定,所以可以消去 b ,那么就要求 x 不能被 整除。那么首先,因为 s[1] 和 s[n] 都要求是1,那么从 s[2] 开始考虑。如果 s[2] 是?,那么肯定是考虑让其等于0,因为此时乘以1不会对是否能被整除造成影响。之后,问题就变为了,需要选择若干个
,让 x 乘以2或 j,使得乘积最小且不能被整除。
之后考虑分类讨论,首先,若 c' 是2的幂,那么对于所有的偶数 j,考虑都选择将其乘以2,因为这样可以使得乘积最小。之后,对于剩下的奇数 j,考虑贪心地选择尽可能多的2,使得乘积最小且不能被 c' 整除。而若 c' 不是2的幂,那么就可以每次都乘2,这样可以保证 x 不能被 c' 整除,且乘积是最小的。
总结
数论还是太难......