A:一道好题
题目描述
有一个长度为 、初始全为 的序列 ,另有一个长度为 的序列 。目标是通过若干次操作将 变成 。
可以执行两种操作:
1 x:将 中所有值为 的数加 。2 x:将 中下标为 的数加 。
不需要最小化操作次数,但操作次数不能超过 。
输入
第一行一个正整数 ,满足 。
第二行 个非负整数 ,满足 。
输出
第一行输出操作次数 ,需要满足 。
之后 行,每行输出一种操作。对于 1 x,需要满足 ;对于 2 x,需要满足 。
分析
一开始尝试过贪心和分块,但容易超过操作次数限制。注意到 大约可以支持 级别的构造,因此可以考虑分治。
在区间 中,先把后半部分 依次加一,这样整体会被分成两个不同部分,再配合第一种操作对整段统一加一。递归处理左右两段,就能把操作次数控制在可接受范围内。
核心思路是:用“整体加一”降低单点操作次数,再通过分治把不同值逐层拆开。
I:不定方程
题目描述
给定正整数 ,求不定方程
的正整数解数量。
输入
第一行一个整数 ,表示询问数量,满足 。
之后 行,每行两个整数 ,表示一次询问,满足 ,。
输出
对于每一组询问,输出一行一个非负整数,表示答案。
分析
利用恒等式:
设 ,也就是 ,带入后得到:
可以看出, 是 的一个因子;同时在 固定时, 关于 单调递增。
因此可以枚举 的因子,再二分寻找对应的 。由于 很大,可以使用 Pollard-Rho 分解质因子,再枚举因子组合。
代码
int n, a[N], c[N];
PII b[N];
vector> op;
void merge(int l, int r, int val)//val:此时整个区间的值
{
if (b[r].f == val)
return;
int mid = l + r + 1 >> 1, now = val;
if (b[mid].f > val)
{
for (int i = mid; i now)
merge(mid, r, now);
merge(l, mid - 1, val);
}
void solve()
{
cin >> n;
for (int i = 1; i > a[i], b[i] = {a[i], i};
sort(b + 1, b + 1 + n);
merge(1, n, 0);
cout v[N];
int p[N], ff = 0, q[N], cnt = 0;
bool st[N];
void dfs(int u, int f)
{
for(int it : v[u])
{
if(it == f) continue;
if(ff) return;
p[it] = u;
// cout > n;
for(int i = 1; i > x >> y;
v[x].push_back(y);
v[y].push_back(x);
num[x] ++;
num[y] ++;
}
dfs(1, 0);
q[cnt + 1] = q[1];
int cnt1 = 0, cnt2 = 0;
for(int i = 1; i > n >> q >> m >> k;
p[0] = 1;
// p2[0] = 1;
for(int i = 1; i > s[i];
int z = s[i].size();
s[i] = ' ' + s[i];
for(int j = 1; j > c[i];
int z = c[i].size();
c[i] = ' ' + c[i];
for(int j = 1; j > 1;
if(check(l, mid, i, j)) l = mid + 1;
else r = mid;
}
if(c[i][l] != s[j][l])
{
cnt ++;
}
lr = l + 1;
if(cnt > k) break;
}
if(cnt >=1;
}
return res;
}
void solve(){
int m,k;cin>>m>>k;
for(int i=1,sum=1;i<=k;i++,sum+=m-1,sum%=P){
int ver=f1[i-1]*qmi(sum,P-2)%P;
f2[i]=f2[i-1]+ver;
f2[i]%=P;
f1[i]=(f1[i-1]+(ver+1)*m%P-ver+P)%P;
}
cout<<(f1[k]+f2[k])%P<<"\n";
}