『题解』Luogu P8318 『JROI-4』淘气的猴子
题目大意
有一个长为 \(n\) 的正整数序列 \(a\),对其进行 \(m\) 次操作后形成序列 \(b\)。
对于每次操作:
1 x y:将 \(a_x\) 加上 \(a_y\)。2 x y:将 \(a_x\) 乘上 \(a_y\)。
特别且显然的,若 \(x=y\),新的 \(a_x\) 就等于原来的 \(a_x\) 的两倍或平方。
现在给出 \(n\),序列 \(b\) 和依次执行的 \(m\) 次操作,求序列 \(a\)。
思路
第一次读题,认为只需要反着对 \(b\) 操作一遍就行了,操作顺序反,加法变减法,乘法变除法。
然而...虽然绿色不少,但还是爆零了。
第 \(114514\) 次读题,终于去答疑贴问了一下,下面这句话是什么意思。
特别且显然地,当 \(x=y\),新的 \(x\) 就等于原来的 \(x\) 的两倍或平方。
得知,当 \(x=y\),新的 \(a_x\) 就等于原来的 \(a_x\) 的两倍或平方(什么鬼题面)。
这就知道为什么爆零了,倒着操作的时候,若 \(x=y\),是要用 \(a_y\) 对 \(a_x\) 操作的。可是 \(x=y\) 啊,\(a_x\) 就是 \(a_y\)。这就导致了取不到想要的 \(a_y\):若是操作 \(1\),\(a_x\) 将减去 \(a_x\),变为 \(0\);若是操作 \(2\),\(a_x\) 要除以 \(a_x\),变为 \(1\)。
这个坑,我花了 1.5h 才爬出来/kk。
两种操作分别写个特判,判 \(x=y\) 的情况。对于每种操作:
- 操作 \(1\):相当于两个原来的 \(a_x\) 相加,于是除以二即可。
- 操作 \(2\):相当于两个原来的 \(a_x\) 相乘,于是开平方即可。
嗯,这道题就做完了。
代码
#include <iostream>
#include <cmath>
using namespace std;
template<typename T=long long>
inline T read(){
T X=0; bool flag=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') flag=0; ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<1)+(X<<3)+ch-'0',ch=getchar();
if(flag) return X;
return ~(X-1);
}
template<typename T=int>
inline void write(T X){
if(X<0) putchar('-'),X=~(X-1);
T s[20],top=0;
while(X) s[++top]=X%10,X/=10;
if(!top) s[++top]=0;
while(top) putchar(s[top--]+'0');
putchar(' ');
}
typedef long long ll;
const int N=1e3+5,M=205;
ll n,m,a[N];
int k[M],x[M],y[M];
int main(){
n=read(),m=read();
for(int i=1; i<=n; i++){
a[i]=read();
}
for(int i=1; i<=m; i++){
k[i]=read(),x[i]=read(),y[i]=read();
}
for(int i=m; i>=1; i--){
if(k[i]==1){
if(x[i]==y[i]){
a[x[i]]/=2;
}else{
a[x[i]]-=a[y[i]];
}
}else{
if(x[i]==y[i]){
a[x[i]]=sqrt(a[x[i]]);
}else{
a[x[i]]/=a[y[i]];
}
}
}
for(int i=1; i<=n; i++){
write(a[i]);
}
return 0;
}

浙公网安备 33010602011771号