[CSP-S膜你赛]Day1 2019.11.11(暴力大作战)

T1

题意

给出(a,b)两个数的异或、与、或值,求有序数对((a,b))的种数,如果值为-1则表示不确定,保证三个值不全为-1,无数解输出inf

解法

签到题,氵氵氵

首先有(or-and=xor),如果有两个数则相当于三个数都已知;

(count(i))表示(i)在二进制下1的个数

  1. 只知道一个数:(and,xor:inf;or:3^{count(or)})

  2. 三个数都知道:将每一位分开算,每当存在((or=1,and=0))时答案乘2

注意判断无解即可。。。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,AND,OR,XOR;

template<class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=(x*10+c-48); x*=sign;
}

int main()
{
	freopen("bit.in","r",stdin);
	freopen("bit.out","w",stdout);
	read(T);
	while(T--)
	{
		read(AND);read(OR);read(XOR);
		if(OR==-1&&XOR==-1) puts("inf");
		else if(AND==-1&&OR==-1) puts("inf");
		else if(AND==-1&&XOR==-1)//sum of 1
		{
			ll sum=1;
			for(int i=30;i>=0;--i) if(OR>>i&1) sum*=3;
			cout<<sum<<endl;
		}
		else
		{
			if(AND==-1) AND=OR-XOR;
			else if(OR==-1) OR=AND+XOR;
			else if(XOR==-1) XOR=OR-AND;
			if(AND<0||OR<0||XOR<0||AND>OR||OR-AND!=XOR) puts("0");
			else
			{
				ll sum=1;
				for(int i=30;i>=0;--i)
				{
					int ab=(AND>>i&1),ob=(OR>>i&1),xb=(XOR>>i&1);
					if((!ab) && ob && xb) sum*=2;
					else if(ab && ob && (!xb)) sum*=1;
					else if((!ab) && (!ob) && (!xb)) sum*=1;
					else { sum=0; break; }
				}
				cout<<sum<<endl;
			}
		}
	}
	return 0;
}

T2

题意

给一个开始为空的集合,支持与一个给出的新集合求交/并,以及给集合中所有元素加/减1,(sum)新元素(leq 3e6)

解法

签到题2,水沝淼

一开题目,bitset?不会用,只能打暴力

显然开个桶表示集合,只要保证复杂度只与新元素的个数有关既可保证不超时

(如果用(set)的话,时间复杂度与集合中元素个数有关,可能会T)

  1. 取并集:对于一个新元素,如果它不存在于桶中,则加入桶,计算贡献,否则不管

  2. 取交集(我的做法):清空原集合所有元素,然后加入交集中的所有元素

  3. 加、减1,用个标记(tmp),一个元素加入集合时先加(tmp),输出答案时减去(tmp),加减1对(tmp)加减即可

代码跟C++入门题一样。。。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,xx[3000005];
ll ans,siz,bt,tmp;//cout<<ans-siz*tmp,tmp表示每个数需要减去的数值 
bool sum[5000005],apr[5000005];
int st[5000005],top;

template<class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=(x*10+c-48); x*=sign;
}

int main()//一个数解码x-temp,编码=x+temp
{
	freopen("jihe.in","r",stdin);
	freopen("jihe.out","w",stdout);	
	bt=tmp=2000005;
	ans=siz=0;
	read(T);
	while(T--)
	{
		int k;read(k);
		if(k==1)//并 
		{
			int m,x; read(m);
			for(int i=1;i<=m;++i)
			{
				read(x);
				x+=tmp;
				if(!sum[x])
				{
					sum[x]=1;
					ans+=x;
					++siz;
					st[++top]=x;
				}
			}
		}
		else if(k==2)//交 
		{
			int m; read(m);
			for(int i=1;i<=m;++i)//打标记 
			{
				read(xx[i]);
				xx[i]+=tmp;
				apr[xx[i]]=1;
			}
			for(int i=1;i<=top;++i)
			{
				if(!apr[st[i]])//没有出现 
				{
					sum[st[i]]=0;
					ans-=st[i];
					--siz;
				}
			}
			top=0;
			for(int i=1;i<=m;++i) if(sum[xx[i]]) st[++top]=xx[i];
			for(int i=1;i<=m;++i) apr[xx[i]]=0;//置零 
		}
		else if(k==3) --tmp;
		else ++tmp;
		printf("%lld
",ans-siz*tmp);
	}
	return 0;
}

T3

题意

一个(n imes m)的网格中有一些不同种类的方块,没有方块的位置都为空格,两个方块可以成对,当且仅当它们种类相同且存在一条只由空格构成的路径连接它们,(n,mleq 1000)

解法

最容易想到的方法是枚举方块,再bfs一遍可以走到的位置,(O(n^2m^2))

好一些的方法是枚举极大的白色连通块,一个白色相邻的白色连通块所连接到的相同种类方块均可以成对;但是是否有正确性?

这样做显然不对,因为同一对方块可能被计算多次,即同时有多个白色连通块同时连接它们;

一个方块最多与四个白色连通块相接,所以可以考虑容斥
枚举一个方块的贡献,答案为“与一个白色相接的答案-两个+三个-四个”(如果两个方块同时与这些白色块相连,那么它们可以算作一对)

用一个结构体储存连接情况,一个方块最多与四种方块连接,分集合有15种,所以一个方块最多15个结构体,对这些结构体排个序就可以找到完全相同的结构体,它们可以成对,数量为(C(sum,2))

如果两个同类型格子之间直接连接,可以假装在它们之间加一个很小的白色块,继续套上面的做法就行了

直接快速排序是(O(4 imes 15 imes nmlog(15nm))),会超时(虽然数据没卡就对了,但实际上全1的网格可以卡掉),需要换成基数排序

Code(快速排序)

#include<bits/stdc++.h>
#define N 1005 
using namespace std;
typedef long long ll;
int n,m,k,a[N][N];
int dox[4]={0,-1,0,1},doy[4]={-1,0,1,0};
int co[N][N],mp[N][N][4],ndsum;//白色块 
ll ans=0;
struct Node
{
	int opt,color,id[5];
}nd[N*N*16];int cnt;

template<class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=(x*10+c-48); x*=sign;
}
void dfs(int x,int y)//对白色块染色 
{
	co[x][y]=ndsum;
	for(int i=0;i<4;++i)
	{
		int dx=dox[i]+x,dy=doy[i]+y;
		if(dx<1||dy<1||dx>n||dy>m||a[dx][dy]) continue;
		if(!co[dx][dy]) dfs(dx,dy);
	}
}

bool cmp(Node a,Node b)
{
	if(a.id[1]!=b.id[1]) return a.id[1]<b.id[1];
	if(a.id[2]!=b.id[2]) return a.id[2]<b.id[2];
	if(a.id[3]!=b.id[3]) return a.id[3]<b.id[3];
	if(a.id[4]!=b.id[4]) return a.id[4]<b.id[4];
	return a.color<b.color;
}
int main()
{
	freopen("link.in","r",stdin);
	freopen("link.out","w",stdout);
	read(n);read(m);read(k);
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=m;++j)
		read(a[i][j]);
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=m;++j)
	    if(!a[i][j]&&!co[i][j]) 
		  ++ndsum,dfs(i,j);
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=m;++j)
	    if(a[i][j])
	    {
	    	int st[5]={0},top=0;
	    	for(int k=0;k<4;++k)
	    	{
	    		int dx=dox[k]+i,dy=doy[k]+j;
	    		if(dx<1||dy<1||dx>n||dy>m) continue;
	    		if(!a[dx][dy]) mp[i][j][k]=co[dx][dy];
	    		else 
	    		{
	    			if(a[dx][dy]!=a[i][j]) continue;
	    			if(k==2||k==3) mp[i][j][k]=++ndsum;
	    			else mp[i][j][k]=mp[dx][dy][k+2];
				} 
	    		bool ok=1;
	    		for(int c=1;c<=top;++c) if(st[c]==mp[i][j][k]) ok=0;
	    		if(ok) st[++top]=mp[i][j][k];
	    	}
	    	sort(st+1,st+top+1);
	    	for(int sta=1;sta<(1<<top);++sta)
	    	{	
				nd[++cnt].color=a[i][j];
	    		for(int c=0;c<top;++c)
				  if(sta>>c&1) nd[cnt].id[++nd[cnt].opt]=st[c+1];
	    	}
	    }
	sort(nd+1,nd+cnt+1,cmp);
	ll ccf=0;
	for(int i=1;i<=cnt;++i)
	{
		bool ok=1;
		for(int j=1;j<=4;++j) if(nd[i].id[j]!=nd[i-1].id[j]) ok=0;
		if(nd[i].color!=nd[i-1].color) ok=0;
		if(!ok&&i!=1)
		{
			if(nd[i-1].opt&1) ans+=(ccf-1)*ccf/2;
			else ans-=(ccf-1)*ccf/2;
			ccf=0;
		}
		++ccf;
	}
	if(ccf) ans+=(nd[cnt].opt&1 ? 1:-1)*(ccf-1)*ccf/2;
	cout<<ans<<endl;
	return 0;
}

或者也可以特殊处理相邻块同色的情况,即加两个相邻格子的贡献当且仅当没有它们不能通过白色格子相通,见std

std

#include<bits/stdc++.h>
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int maxn=1010;
typedef std::pair<int,int>pos;
#define x first
#define y second
int n,m;
inline bool valid(pos tmp){
	return tmp.x>=0&&tmp.x<n&&tmp.y>=0&&tmp.y<m;
}
inline pos operator+(const pos&a,const pos&b){
	return pos(a.x+b.x,a.y+b.y);
}
inline int to_ind(pos curr){
	return curr.x*m+curr.y;
}
int fa[maxn*maxn];
inline int find(int x){
	return(x==fa[x])?(x):(fa[x]=find(fa[x]));
}
inline int find(pos tmp){
	int x=to_ind(tmp);
	return(x==fa[x])?(x):(fa[x]=find(fa[x]));
}
inline void uni(int a,int b){
	fa[find(a)]=find(b);
}
inline void uni(pos a,pos b){
	fa[find(to_ind(a))]=find(to_ind(b));
}
const pos exc[]={{-1,0},{1,0},{0,-1},{0,1}};
typedef unsigned long long ull;
std::map<ull,int>ms;
const ull hashlevel=998244353;
typedef long long ll;
int valbase[maxn][maxn];
#define val(tmp) valbase[tmp.first][tmp.second]
int main(){
//	freopen("link.in","r",stdin);
//	freopen("link.out","w",stdout);
	n=read();m=read();read();
	pos curr,target;
	for(curr.x=0;curr.x<n;curr.x++)
		for(curr.y=0;curr.y<m;curr.y++)
			val(curr)=read();
	for(int i=0;i<n*m;i++)
		fa[i]=i;
	for(curr.x=0;curr.x<n;curr.x++)
		for(curr.y=0;curr.y<m;curr.y++)
			if(!val(curr))
				for(int k=0;k<4;k++)
					if(valid(target=curr+exc[k])&&!val(target))
						uni(curr,target);
	ll ans=0;
	int tmp[4];
	for(curr.x=0;curr.x<n;curr.x++)
		for(curr.y=0;curr.y<m;curr.y++)
			if(val(curr)){
				int cnt=0;
				for(int k=0;k<4;k++)
					if(valid(target=curr+exc[k])&&!val(target))
						tmp[cnt++]=find(target);
				std::sort(tmp,tmp+cnt);
				cnt=std::unique(tmp,tmp+cnt)-tmp;
				for(int k=1;k<(1<<cnt);k++){
					int choice=0;
					ull hash=0;
					for(int p=0;p<cnt;p++)
						if((1<<p)&k){
							++choice;
							(hash*=hashlevel)+=tmp[p];
						}
					(hash*=hashlevel)+=val(curr);
					int contribution=++ms[hash];
					ans+=(contribution-1)*((choice&1)?1:-1);
				}
			}
	std::set<int>s;
	int match=0;
	pos thi;
	for(curr.x=0;curr.x<n;curr.x++)
		for(curr.y=0;curr.y<m;curr.y++)
			if(val(curr)){
				s.clear();
				for(int k=0;k<4;k++)
					if(valid(target=curr+exc[k])&&!val(target))
						s.insert(find(target));
				for(int k=0;k<4;k++)
					if(valid(target=curr+exc[k])&&val(target)==val(curr)){
						++match;
						for(int p=0;p<4;p++)
							if(valid(thi=target+exc[p])&&!val(thi)&&s.count(find(thi))){
								--match;
								break;
							}
					}
			}
	std::cout<<ans+(match>>1);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

原文地址:https://www.cnblogs.com/Chtholly/p/11834870.html