题目来源#
【LGR-093】洛谷 10 月月赛 I & MCOI R6 Div.1 ↗ A题
【LGR-093】洛谷 10 月月赛 I & MCOI R6 Div.2 ↗ C题
可能存在一个非负整数数序列 a1,a2,…,an 使得 0≤ai<109+7。
给定 x1,x2,⋯,xn,y1,y2,⋯,yn,z1,z2,⋯,zn,已知对于 1≤i≤n 满足:
xi(j=1∑iaj)+yi(j=i∑naj)≡zi(mod109+7)
先求满足条件的解的个数。若只有一组解,求出 a1,a2,…,an。
有多测。
对于 10% 的数据,n=1,
对于另外 19% 的数据,n≤100,
对于另外 19% 的数据,xi=yi=1,
对于另外 22% 的数据,保证有唯一解。
对于 100% 的数据,1≤n≤2×105,1≤xi,yi≤109+7,0≤zi≤109+7.
Subtask 1#
对于 10% 的数据,n=1.
按照题意写出关于 a1 的方程:
x1a1+y1a1≡z1(mod109+7)
即:
(x1+y1)a1≡z1(mod109+7)
注意到此时 a1 不一定有唯一解,需要分类讨论。
一、 当 x1+y1=0 且 z1=0 时,方程无解。
二、 当 x1+y1=0 且 z1=0 时,a1 可以在 [0,109+7) 上任取,共有 109+7 组解。
三、 对于其他情况均有唯一解:
a1=x1+y1z1(mod109+7)
用乘法逆元处理即可。
if(n == 1) cout << 1 << endl << z[1] * inv((x[1] + y[1]) % mod) % mod << endl;
cpp
这里如果只考虑情况三,也可以拿到这 10 分。
但由于题目有多组测试数据,如果后续在代码中保留对 n=1 的特判,不分类讨论会被卡掉。
然后还查不出来)
Subtask 3#
对于另外 19% 的数据,xi=yi=1.
为了方便表示,后面涉及的所有运算都在 mod109+7 意义下进行。
把 n 个方程列出:
⎩⎨⎧x1(a1)+y1(a1+a2+⋯+an)=z1x2(a1+a2)+y2(a2+⋯+an)=z2x3(a1+⋯+a3)+y3(a3+⋯+an)=z3⋯xn(a1+a2+⋯+an)+yn(an)=zn
若 xi=yi=1,那么:
⎩⎨⎧(a1+a2+⋯+an)+a1=z1(a1+a2+⋯+an)+a2=z2(a1+a2+⋯+an)+a3=z3⋯(a1+a2+⋯+an)+an=zn(1)(2)(3)⋯(n)
容易想到把 n 个方程相加,
(n+1)(a1+a2+⋯+an)=z1+z2+⋯+zn
把 n+1 除过去就能得到 a1+a2+⋯+an 的值,再代入 (1)⋯(n) 式即可求出答案。
同样的,需要分类讨论一下解的情况。但对于这部分数据,不讨论也能通过。
if(subtask3) {
int sumz = 0;
for(int i = 1; i <= n; i++) (sumz += z[i]) %= mod;
int sum = sumz * inv(n + 1) % mod;
cout << 1 << endl;
for(int i = 1; i <= n; i++) {
cout << (((z[i] - sum) % mod) + mod) % mod << " ";
} cout << endl;
}
cpp
Subtask 2#
对于另外 19% 的数据,n≤100。
⎩⎨⎧x1(a1)+y1(a1+a2+⋯+an)=z1x2(a1+a2)+y2(a2+⋯+an)=z2x3(a1+⋯+a3)+y3(a3+⋯+an)=z3⋯xn(a1+a2+⋯+an)+yn(an)=zn
对于这个方程组,我们可以把每个方程进行整理,转化为这样的形式:
⎩⎨⎧c1,1a1+c1,2a2+⋯+c1,nan=z1c2,1a1+c2,2a2+⋯+c2,nan=z2⋯cn,1a1+cn,2a2+⋯+cn,nan=zn
也就是把每个方程中,未知数的系数求出来。
之后使用高斯消元 ↗直接解方程。
过程中,若存在自由元,则为解的个数为 109+7,不管有几个自由元都是。不知道为什么。
若方程组通过化简可以得到 0⋅x=0,那么 x 的取值任意,称 x 为自由元。
时间复杂度 O(n3).
Subtask 4#
对于另外 22% 的数据,保证有唯一解。
题目中的方程显然不同于随机生成的方程,要找到一些可以利用的特性。
有可能可以发现,方程与区间和密切相关,于是考虑前缀和。
设 si=∑j=1jaj,则方程组通项为:
xi⋅si+yi(sn−sj−1)=zi(i)
也就是:
−yi⋅sj−1+xi⋅si+yi⋅sn=zi(i)
这种形式下,每个方程涉及的未知数更少,更容易处理。
考虑系数矩阵的形态:
1234561xx2xx3xx4xx5xx6xxxxxx
x 表示系数不为 0。
这样第 1 行只需要消第 2 行,第 2 行消第 3 行,以此类推,最后的矩阵形如:
1234561x02x03x04x05x06xxxxxx
于是 an 可求,分别代入第 1∼n−1 个方程即可求解。
这样的时间复杂度是 O(nlogn),logn 来自求逆元。
对于空间,每行只有 3 个系数需要存,不用开矩阵,也是 O(n) 的。
Subtask 5#
对于 100% 的数据,1≤n≤2×105,1≤xi,yi≤109+7,0≤zi≤109+7.
上面的时空复杂度已经可以通过此题,只需要多加一个判断解的个数。
判断的方法在 Subtask 1 中提到过了,但需要另外注意:
存在一个自由元,其值可以任取,不代表方程组有无穷组解。只要有一个变量是无解的,则方程组无解。
删去了 read()
和头文件。
#define int long long
const int MAXN = 2e5 + 10, mod = 1e9 + 7;
inline int power(int x, int k) {
int ret = 1;
while(k) {
if(k & 1) ret = ret * x % mod;
x = x * x % mod;
k >>= 1;
}
return ret;
}
inline int inv(int x) {return power(x, mod - 2);}
int T, n;
int x[MAXN], y[MAXN], z[MAXN];
int a[MAXN], b[MAXN], c[MAXN];
int s[MAXN];
signed main() {
T = read();
while(T--) {
n = read();
for(int i = 1; i <= n; i++) {
x[i] = read(); y[i] = read(); z[i] = read();
}
//对每个x,y,z,x[i]s[i] - y[i]s[i-1] + y[i]s[n] = z[i]
for(int i = 1; i < n; i++) {//高斯消元解前缀和
a[i] = mod - y[i]; b[i] = x[i]; c[i] = y[i];
} a[n] = mod - y[n]; c[n] = (x[n] + y[n]) % mod;
for(int i = 1; i < n; i++) {//用第i行消掉第i+1行的第i列
int k = a[i + 1] * inv(b[i]) % mod;
(a[i + 1] -= k * b[i] % mod) %= mod;
(c[i + 1] -= k * c[i] % mod) %= mod;
(z[i + 1] -= k * z[i] % mod) %= mod;
}
int roots = 1;
if(c[n] == 0 && z[n] != 0) {
putchar('0'); putchar('\n');
continue;
}
else if(c[n] == 0 && z[n] == 0) roots = mod;
else s[n] = z[n] * inv(c[n]) % mod;
for(int i = 1; i <= n - 1; i++) {
int right = (z[i] - c[i] * s[n] % mod) % mod;
if(b[i] == 0 && right != 0) {roots = 0; break;}
else if(b[i] == 0 && right == 0) roots += mod;
else s[i] = ((z[i] - c[i] * s[n] % mod) * inv(b[i]) % mod + mod) % mod;
}
if(roots != 1) {printf("%lld\n", roots); continue;}
else {putchar('1'); putchar('\n');}
for(int i = 1; i <= n; i++)
printf("%lld ", ((s[i] - s[i - 1]) % mod + mod) % mod);
putchar('\n');
}
return 0;
}
cpp