题目链接: Codeforces Round #368 (Div. 2) D - Persistent Bookcase
题意:维护一个布尔矩阵s,支持四种操作:
(1) 1,x,y 将s[x][y]赋值为true(放书)
(2) 2,x,y 将s[x][y]赋值为false(取书)
(3) 3,x 将第x行所有值翻转(true->false,false->true)
(4)4,x 回到第x次操作后的情况
每次操作之后输出值为true的个数。
分析:
容易发现,操作形成了一棵树的结构:
(1) 如果第i个操作为1~3类,那么他的父亲是i-1.
(2) 如果是第4类,他父亲为x。
当时想到了这一点,但是并没有想到直接深搜能过(F**k!)。
主意正常的还原现场即可,居然一写就过。
代码如下:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const int maxn=2000+5,maxq=100000+5;
int n,m,q,tot,line[maxn],ans[maxq]; //line表示每行有多少书
int e,last[maxq],Next[maxq],to[maxq];
bool book[maxn][maxn]; //标记有没有书
struct query{
int id,p,x,y;
}ask[maxq];
inline void _read(int &x){
char ch=getchar(); bool mark=false;
for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(mark)x=-x;
}
void Addedge(int x,int y){
to[++e]=y;
Next[e]=last[x];
last[x]=e;
}
void DFS(int x){
int i;
bool flag=false; //flag=true表示当前操作有效
query& q=ask[x];
if(q.p==1&&!book[q.x][q.y]){
tot++; flag=true;
book[q.x][q.y]=true;
line[q.x]++;
}
else if(q.p==2&&book[q.x][q.y]){
tot--; flag=true;
book[q.x][q.y]=false;
line[q.x]--;
}
else if(q.p==3){
flag=true;
tot+=m-2*line[q.x];
line[q.x]=m-line[q.x];
for(i=1;i<=m;i++)book[q.x][i]=!book[q.x][i];
}
ans[q.id]=tot;
for(i=last[x];i!=-1;i=Next[i])DFS(to[i]);
if(!flag) return ;
//还原现场
if(q.p==1){
tot--; book[q.x][q.y]=false;
line[q.x]--;
}
else if(q.p==2){
tot++; book[q.x][q.y]=true;
line[q.x]++;
}
else if(q.p==3){
tot+=m-2*line[q.x];
line[q.x]=m-line[q.x];
for(i=1;i<=m;i++)book[q.x][i]=!book[q.x][i];
}
}
int main(){
int i,j,x,y,z;
_read(n);_read(m);_read(q);
memset(last,-1,sizeof(last));
for(i=1;i<=q;i++){
ask[i].id=i;
_read(ask[i].p);
if(ask[i].p!=4){
Addedge(i-1,i);
_read(ask[i].x);
if(ask[i].p!=3) _read(ask[i].y);
}
else {
_read(x);
Addedge(x,i);
}
}
DFS(0);
for(i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}
E 题目链接: Codeforces Round #368 (Div. 2) E. Garlands
大意: 一个n*m矩阵中有若干个灯泡,组成k个块(逐个给出),每个灯泡有一定的欢乐值。
有以下两种操作:
(1) SWITCH k 改变第K个块中所有灯泡的开情况(一开始全部为打开)。
(2) ASK x1,y1,x2,y2 询问左上(x1,y1),右下(x2,y2)这个矩形中,所有打开的灯泡的欢乐值之和。
分析:
由于ask操作不超过2000个,所以考虑离线操作:
将第i个块落在第j个询问的区域的灯泡的欢乐值之和预处理出来(cnt[i][j]),再记录下每个块的开关情况就好了。
答案就是所有打开的块cnt之和。
快速的修改矩阵中的元素,并查询前缀和,用二维树状数组来维护。
代码如下:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const int maxn=2000+5,maxq=1000000+5;
int n,m,k,q,flip[maxq];
int x1[maxq],x2[maxq],y1[maxq],y2[maxq]; //存的是询问
LL c[maxn][maxn],cnt[maxn][maxn];
int is_on[maxn];
// cnt[i][j]表示第i个连通块在第j个询问范围内的欢乐值总和
struct grid{
int x,y; LL w;
grid(int x,int y,int w):x(x),y(y),w((LL)w){}
};
vector<grid> group[maxn]; //记录每个块
inline void _read(int &x){
char ch=getchar(); bool mark=false;
for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(mark)x=-x;
}
inline int lowbit(int x){return x&-x;}
inline void Add(int x,int y,LL d){
for(int i=x;i<=maxn;i+=lowbit(i)) //注意,这里一定要写maxn!!n会wa得很惨
for(int j=y;j<=maxn;j+=lowbit(j))
c[i][j]+=d;
}
inline LL Sum(int x,int y){
LL ans=0;
for(int i=x;i>0;i-=lowbit(i))
for(int j=y;j>0;j-=lowbit(j))
ans+=c[i][j];
return ans;
}
int main(){
int i,j,len,xx,yy,ww,zz;
_read(n);_read(m);_read(k);
for(i=1;i<=k;i++){
_read(zz);
for(j=0;j<zz;j++){
_read(xx); _read(yy); _read(ww);
group[i].push_back(grid(xx,yy,ww));
}
}
char str[10];
int ask=0;
_read(q);
for(i=1;i<=q;i++){
scanf("%s",str);
if(str[0]=='S')_read(flip[i]);
else {
ask++;
_read(x1[ask]); _read(y1[ask]);
_read(x2[ask]); _read(y2[ask]);
}
}
for(i=1;i<=k;i++){ //预处理cnt数组
for(j=0;j<group[i].size();j++)
Add(group[i][j].y,group[i][j].x,group[i][j].w);
for(j=1;j<=ask;j++) //一个标准的二维前缀和
cnt[i][j]=Sum(y2[j],x2[j])+Sum(y1[j]-1,x1[j]-1)-Sum(y1[j]-1,x2[j])-Sum(y2[j],x1[j]-1);
for(j=0;j<group[i].size();j++)
Add(group[i][j].y,group[i][j].x,-group[i][j].w);
}
for(i=1;i<=k;i++)is_on[i]=1; //最初全部打开
int cur=0;
for(i=1;i<=q;i++){
LL ans=0;
if(flip[i])is_on[flip[i]]^=1; //取反
else {
cur++;
for(j=1;j<=k;j++)
if(is_on[j]) ans+=cnt[j][cur];
cout<<ans<<endl;
}
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<cstring>
using namespace std;
inline void _read(long long &x){
char ch=getchar(); bool mark=false;
for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(mark)x=-x;
}
const long long maxn=2005;
long long n,m,k,q;
struct node{
long long x,y,w;
node(){}
node(long long a,long long b,long long c){x=a;y=b;w=c;}
};
vector<node> a[maxn];
long long work[1000005];
long long c[maxn][maxn];
long long cnt[maxn][maxn];
bool on[maxn];
long long lowbit(long long x){
return x&(-x);
}
void modify(long long x,long long y,long long d){
for(long long i=x;i<=maxn;i+=lowbit(i)){
for(long long j=y;j<=maxn;j+=lowbit(j)){
c[i][j]+=d;
}
}
}
long long getsum(long long x,long long y){
long long sum=0;
for(long long i=x;i;i-=lowbit(i)){
for(long long j=y;j;j-=lowbit(j)){
sum+=c[i][j];
}
}
return sum;
}
long long tot=0;
struct node2{
long long x1,y1,x2,y2;
};
node2 ask[1000005];
int main(){
long long i,j;
cin>>n>>m>>k;
for(i=1;i<=k;i++){
long long len;
_read(len);
for(j=1;j<=len;j++){
long long x,y,w;
_read(x);_read(y);_read(w);
a[i].push_back(node(x,y,w));
}
}
cin>>q;
for(i=1;i<=q;i++){
char s[10];
scanf("%s",s);
if(s[0]=='S'){
_read(work[i]);
}
else{
tot++;
_read(ask[tot].x1);_read(ask[tot].y1);_read(ask[tot].x2);_read(ask[tot].y2);
}
}
for(i=1;i<=k;i++){
for(j=0;j<a[i].size();j++){
modify(a[i][j].x,a[i][j].y,a[i][j].w);
}
for(j=1;j<=tot;j++){
cnt[i][j]=getsum(ask[j].x2,ask[j].y2)+getsum(ask[j].x1-1,ask[j].y1-1)-getsum(ask[j].x2,ask[j].y1-1)-getsum(ask[j].x1-1,ask[j].y2);
}
for(j=0;j<a[i].size();j++){
modify(a[i][j].x,a[i][j].y,-a[i][j].w);
}
}
for(i=1;i<=k;i++)on[i]=true;
long long l=0;
for(i=1;i<=q;i++){
if(work[i]){
on[work[i]]=(!on[work[i]]);
}
else{
long long ans=0;
l++;
for(j=1;j<=k;j++){
if(on[j])ans+=cnt[j][l];
}
printf("%I64d\n",ans);
}
}
}