『题解』Luogu P6974 [NEERC2015]Adjustment Office
题目大意
有一个 \(n \times n\) 的矩阵,每个数的值为那个数所在的行数与列数的和。
接下来给定 \(q\) 个操作,对于每个操作:
R m:输出第 \(m\) 行的总和并删去第 \(m\) 行。C m:输出第 \(m\) 列的总和并删去第 \(m\) 列。
思路
显然,若删去第 \(m\) 行,对于其它行没有任何影响。删除第 \(m\) 列,对其它列没有影响。
我们先只分析删除列,操作行的情况。
假设之前已经删除了 \(k\) 列,列号分别为 \(x_1,x_2,\dots,x_k\),要操作的行号为 \(i\)。
那么第 \(i\) 行的初始元素和即为:
\[\begin{aligned} sum&=(i+1)+(i+2)+\dots+(i+n)\\ &=i \times n+1+2+3+\dots+n\\ &=i \times n+\dfrac{n \times (n+1)}{2}\end{aligned}
\]
那么很容易得到在删除了 \(k\) 列后的第 \(i\) 行:
\[\begin{aligned} ans&=i \times n+\dfrac{n \times (n+1)}{2}-(i+x_1)-(i+x_2)-\dots-(i+x_k)\\ &=i \times (n-k)+\dfrac{n \times (n+1)}{2}-x_1-x_2-\dots-x_k\end{aligned}
\]
那么,可以开两个变量 l,ls 分别记录已经删除了多少列(\(k\))和删除的列数的总和(\(x_1+x_2+\dots+x_k\)),这样就可以 \(\mathcal{O}(1)\) 地算出每个操作的答案了。
列也是同理,两个变量 h,hs 分别记录删了多少行和删除的行数的总和。
还要注意已经删过行或列却还删了一次的情况,开两个数组 bh,bl 分别记录。
代码
#include <iostream>
using namespace std;
template<typename T=int>
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('\n');
}
typedef long long ll;
const int N=1e6+5;
ll n,q,x,h,l,hs,ls;
char c;
bool bh[N],bl[N];
int main(){
n=read(),q=read();
while(q--){
cin >> c;
x=read();
if(c=='R'){
if(bh[x]){
puts("0");
continue;
}
write(x*(n-ls)-l+(1+n)*n/2);
hs++;
h+=x;
bh[x]=1;
}else{
if(bl[x]){
puts("0");
continue;
}
write(x*(n-hs)-h+(1+n)*n/2);
ls++;
l+=x;
bl[x]=1;
}
}
return 0;
}

浙公网安备 33010602011771号