Codeforces Round #368 (Div. 2) D &E

24 篇文章 0 订阅
19 篇文章 0 订阅

题目链接: 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);
		}
	}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值