Codeforces 1397C - Multiples of Length (思维)
题意
给定一个数列\(\{a\}\)
每次你需要选中一个区间\([L,R]\)
然后对区间内每个数字进行一次操作,让\(a_i\)变成\(a_i+k\),并且满足\(k\ mod\ (R-L+1)=0\)
你只能严格做三遍操作,使得最终所有数字变成\(0\)
限制
Time limit per test: 1 second
Memory limit per test: 256 megabytes
输入限制
\(1\leq n\leq 10^5\)
\(-10^9\leq a_i\leq 10^9\)
输出限制
\(1\leq l,r\leq n\)
\(-10^{18}\leq b_i\leq 10^{18},\ b_i\ mod\ (r-l+1)=0\)
思路一(复杂版)
这个版本的思路主要是为了应对输出更为严格的限制下的题目
但本题的输出要求为\(±10^{18}\)以内(跟没限制一样)
所以这里可能会讲得过于麻烦
只想看本题能过的简单版可以下拉到思路二
该部分输出的值可以证明一定在\(±[(10^5-1)+(10^5-1)\times(10^5-1)+10^9]≈±1.1\times10^{10}\)以内
进一步对正负进行讨论后可以优化到\(±10^{10}\)以内(但我没进一步讨论qwq)
引入 1
假设\(m\)是奇数,那么\(m-1\)是偶数
对于某个数\(x\in [0,m-1)\),可以得到\((x+m)\ mod\ (m-1)=(x+1)\ mod\ (m-1)\)
观察到在该式子中,加上\(m\)的意义与加上\(1\)的意义相同
所以可以得到\([x+(m-1-x)*m]\ mod\ (m-1)=0\)
也就是对于\(x\),加上\(m-1-x\)倍的\(m\)后得到的值取模\(m-1\)等于\(0\)
引入 2
假设\(m\)是偶数,那么\(m-1\)是奇数
对于某个数\(x\in [1,m)\),可以得到\([x+(m-1)]\ mod\ m=(x-1)\ mod\ m\)
观察到在该式子中,加上\(m-1\)的意义与减去\(1\)的意义相同
所以可以得到\([x+x*(m-1)]\ mod\ m=0\)
也就是对于\(x\),加上\(x\)倍的\(m-1\)后得到的值取模\(m\)等于\(0\)
得到的结论
一个数,加上一个奇数的倍数,最终肯定能表示成一个偶数的倍数
本题
根据上述结论,可以根据总长度\(n\)的奇偶性进行分类讨论
如果总长度\(n\)为奇数
①
我们可以直接拿区间\([1,n]\)进行处理
将所有数字加上\(n\)(奇数)的倍数,并表示成\(n-1\)(偶数)的倍数
首先将数字转移到\([0,n)\)内
tmp=(a[i]%n+n)%n;
再计算要加上多少倍,并加上这些倍数
tmp+=((n-1)-tmp)*n;
此时的\(tmp\)就表示成了\(n-1\)的倍数,且\((tmp-a[i])\ mod\ n=0\)
这一步就将\(a[i]\)变成\(tmp\),输出差值\(tmp-a[i]\)即可
②
因为上面一步将所有数字全部变成了\(n-1\)的倍数
那么这一步只需要选择\([1,n-1]\)这个区间,将所有数字全部变成\(0\)并输出差值即可
③
最后选择\([n,n]\)单个数字变成\(0\)并输出差值即可
如果总长度\(n\)为偶数
①
我们可以先拿区间\([1,n-1]\)进行处理
将所有数字加上\(n-1\)(奇数)的倍数,并表示成\(n\)(偶数)的倍数
首先将数字转移到\([0,n-1)\)内
tmp=(a[i]%(n-1)+(n-1))%(n-1);
然后直接加上倍数即可
tmp+=tmp*(n-1);
这里的操作同上,只需要输出差值\(tmp-a[i]\)即可,并将\(a[i]=tmp\)
②
然后选择区间\([n,n]\),将这个数字直接变成\(0\)并输出差值即可
③
最后选择区间\([1,n]\),将所有数字变成\(0\)后输出差值即可
代码一
(93ms/1000ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100050];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
if(n==1)
{
puts("1 1");
printf("%lld\n",-a[1]);
puts("1 1");
puts("0");
puts("1 1");
puts("0");
return 0;
}
if(n&1) //长度为奇数,选一遍消除两遍
{
printf("%d %d\n",1,n);
for(int i=1;i<=n;i++)
{
if(a[i]%(n-1)==0) //已经是n-1的倍数的话则无需进行操作
printf("0 ");
else
{
ll tmp=(a[i]%n+n)%n;
tmp+=((n-1)-tmp)*n;
printf("%lld ",tmp-a[i]); //先输出差值
a[i]=tmp; //再替换
}
}
putchar('\n');
printf("%d %d\n",1,n-1);
for(int i=1;i<n;i++)
printf("%lld ",-a[i]); //输出相反数即可,下同
putchar('\n');
printf("%d %d\n",n,n);
printf("%lld\n",-a[n]);
}
else //长度为偶数,选两遍消除一遍
{
printf("1 %d\n",n-1);
for(int i=1;i<n;i++)
{
if(a[i]%n==0)
printf("0 ");
else
{
ll tmp=(a[i]%(n-1)+(n-1))%(n-1);
tmp+=tmp*(n-1);
printf("%lld ",tmp-a[i]);
a[i]=tmp;
}
}
putchar('\n');
printf("%d %d\n",n,n);
printf("%lld\n",-a[n]);
a[n]=0;
printf("%d %d\n",1,n);
for(int i=1;i<=n;i++)
printf("%lld ",-a[i]);
putchar('\n');
}
return 0;
}
思路二(简单版)
明显的,对于某个数字\(x\)
\(n\times x\)肯定是\(n\)的倍数
且\(n\times x-x=(n-1)\times x\),差值也肯定是\(n-1\)的倍数
那么只需要选择一段长度为\(n-1\)的区间,将所有数字乘以\(n\)
再将单独的那个数字变成\(0\)
最后将所有数字变成\(0\)即可
对于这种方法,输出的最大值约为\(10^9\times 10^5=10^{14}\),在输出允许的范围内
代码二
(109ms/1000ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100050];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
if(n==1)
{
puts("1 1");
printf("%lld\n",-a[1]);
puts("1 1");
puts("0");
puts("1 1");
puts("0");
return 0;
}
printf("%d %d\n",1,n-1);
for(int i=1;i<n;i++)
{
printf("%lld ",a[i]*(n-1));
a[i]*=n;
}
putchar('\n');
printf("%d %d\n",n,n);
printf("%lld\n",-a[n]);
a[n]=0;
printf("%d %d\n",1,n);
for(int i=1;i<=n;i++)
printf("%lld ",-a[i]);
putchar('\n');
return 0;
}