C. Travelling Salesman and Special Numbers
思路:由题意可得,n最多有1000位,那么进行了第一次操作后,它就变成一个不大于1000的数,所以我们可以先预处理1000以内变成1的次数。
第二步,我们再分类讨论,设i是它当前位,i之前有x个1,y到1的操作数为k-1,Size是它的长度
如果第i位是1,那么ans+=第i位为0的所有满足题意的数。也就是Size-i-1的位置中选出y-x个位置放1.也就是C[Size-i-1][y-x]。往后走,x++,i++。
如果第i为为0,则continue。。
这样算其实可能多算了一个,也可能少算了一个。
多算一个什么呢,如果k是1的话,其实它会把1这个数也算进去。
少算什么呢,它本身这个数没有被考虑进去,之前算的都是小于它的数的方案数。
代码如下:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll C[1005][1005];
int dp[1005];
int ones(int num)
{
int cnt = 0;
while (num)
{
if (num % 2)cnt++;
num /= 2;
}
return cnt;
}
void init()
{
for (int i = 0;i <= 1000;i++)C[i][0] = 1;
for (int i = 1;i <= 1000;i++)
for (int j = 0;j <= i;j++)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1])%mod;
dp[1] = 0;
for (int i = 2;i <= 1000;i++)
dp[i] = dp[ones(i)] + 1;
}
vector<int>V;
int main()
{
ios::sync_with_stdio(0);
string n;
int k;
cin >> n >> k;
int Size = n.size();
int used = 0;
init();
ll ans = 0;
if (k == 0)
{
puts("1");
return 0;
}
for (int i = 1;i <= 1000;i++)
if (dp[i] == k - 1)
V.push_back(i);
for (int i = 0;i < Size;i++)
{
if (n[i] == '0')continue;
for (auto it : V)
{
int nouse = Size - i - 1;
if (it-used > nouse||used>it)continue;
ans = (ans + C[nouse][it-used]) % mod;
}
used++;
}
if (k == 1)
ans = (ans - 1 + mod) % mod;
if (dp[used] == k - 1)
ans = (ans + 1) % mod;
cout << ans << endl;
}
D. Bash and a Tough Math Puzzle
思路:如果就是简单的修改询问,我们可以用线段树来维护区间gcd,但题目的询问基于区间内有一个数可以被修改的情况下的。这时候我们可以自顶向下来查找我们需要的区间,如果它的两个子区间的gcd%x都不为0,那就不用往下走了,直接NO,如果只有1个就往下走,记录不满足题意的叶子结点个数tmp。如果tmp>1就是NO,否则YES。
#include<iostream>
using namespace std;
const int maxn = 5e5 + 10;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int Gcd[maxn << 2];
int A[maxn], n;
int gcd(int x, int y)
{
return x == 0 ?y : gcd(y%x, x);
}
void PushUp(int rt)
{
Gcd[rt] = gcd(Gcd[rt << 1], Gcd[rt << 1 | 1]);
}
void Build(int l, int r, int rt)
{
if (l == r)
{
Gcd[rt] = A[l];
return;
}
int m = (l + r) >> 1;
Build(ls);
Build(rs);
PushUp(rt);
}
void Update(int L, int C, int l, int r, int rt)
{
if (l == r)
{
Gcd[rt] = C;
return;
}
int m = l + r >> 1;
if (L <= m)Update(L, C, ls);
else
Update(L, C, rs);
PushUp(rt);
}
int Query(int L, int R, int l, int r, int rt)
{
if (L <= l&&r <= R)
return Gcd[rt];
int m = l + r >> 1;
int ANS = 0;
if (L <= m)
ANS = gcd(ANS, Query(L, R, ls));
if (R > m)
ANS = gcd(ANS, Query(L, R, rs));
return ANS;
}
int tmp;
void check(int L, int R, int x, int l, int r, int rt)
{
if (l == r)
{
if (Gcd[rt] % x != 0)
tmp++;
return;
}
if (L <= l&&r <= R)
{
int m = l + r >> 1;
if (Gcd[rt << 1] % x != 0 && Gcd[rt << 1 | 1] % x != 0)
tmp += 2;
else if ((Gcd[rt << 1] % x != 0))
check(L, R, x, ls);
else if (Gcd[rt << 1 | 1] % x != 0)
check(L, R, x, rs);
return;
}
int m = l + r >> 1;
if (L <= m)
check(L, R, x, ls);
if (R > m)
check(L, R, x, rs);
}
int main()
{
//ios::sync_with_stdio(0);
cin >> n;
for (int i = 1;i <= n;i++)
cin >> A[i];
int Q;
cin >> Q;
int opt;
int l, r, x, y, j;
Build(1, n, 1);
for (int i = 1;i <= Q;i++)
{
scanf("%d", &opt);
if (opt == 1)
{
scanf("%d %d %d", &l, &r, &x);
int temp = Query(l, r, 1, n, 1);
if (temp == x)
puts("YES");
else
{
tmp = 0;
check(l, r, x, 1, n, 1);
if (tmp == 1)
puts("YES");
else
puts("NO");
}
}
else
{
scanf("%d %d", &j,&y);
Update(j, y, 1, n, 1);
}
}
return 0;
}