T1
题意
给出(a,b)两个数的异或、与、或值,求有序数对((a,b))的种数,如果值为-1则表示不确定,保证三个值不全为-1,无数解输出inf
解法
签到题,氵氵氵
首先有(or-and=xor),如果有两个数则相当于三个数都已知;
设(count(i))表示(i)在二进制下1的个数
-
只知道一个数:(and,xor:inf;or:3^{count(or)})
-
三个数都知道:将每一位分开算,每当存在((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,用个标记(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;
}