2020 icpc 济南

  1. Matrix Equation 高斯消元

大意:给定 01 矩阵 A,C

求满足 A × B = B ⊙ C A\times B=B\odot C A×B=BC 01 矩阵 B 的个数。

× \times × : D i , j = ∑ k = 1 n A i , k B k , j D_{i,j}=\sum_{k=1}^n{A_{i,k}B_{k,j}} Di,j=k=1nAi,kBk,j

⊙ \odot : D i , j = A i , j B i , j D_{i,j}=A_{i,j}B_{i,j} Di,j=Ai,jBi,j

思路:通过观察上述式子我们发现 D i , j D_{i,j} Di,j 叉乘 是 k 在变化,列是不变的,对于B的各个列之间是相互独立的。所以我们可以对 B 的每一列单独拉出来考虑。对于 B 的第 j 列 记为 b

根据式子我们可以列出 :

( A 1 , 1 b 1 + A 1 , 2 b 2 + . . . . . . A 1 , n b n ) m o d    2 = A 1 , j b 1 (A_{1,1}b_1+A_{1,2}b_2+......A_{1,n}bn)\mod2=A_{1,j}b_1 (A1,1b1+A1,2b2+......A1,nbn)mod2=A1,jb1

( A 2 , 1 b 1 + A 2 , 2 b 2 + . . . . . . A 2 , n b n ) m o d    2 = A 2 , j b 2 (A_{2,1}b_1+A_{2,2}b_2+......A_{2,n}bn)\mod2=A_{2,j}b_2 (A2,1b1+A2,2b2+......A2,nbn)mod2=A2,jb2

( A n , 1 b 1 + A n , 2 b 2 + . . . . . . A n , n b n ) m o d    2 = A n , j b n (A_{n,1}b_1+A_{n,2}b_2+......A_{n,n}bn)\mod2=A_{n,j}b_n (An,1b1+An,2b2+......An,nbn)mod2=An,jbn

有 mod 2 的存在,所以也就等价于异或线性方程组:

A 1 , 1 b 1 ⊕ A 1 , 2 b 2 ⊕ . . . . . . A 1 , n b n = A 1 , j b 1 A_{1,1}b_1\oplus A_{1,2}b_2\oplus......A_{1,n}bn=A_{1,j}b_1 A1,1b1A1,2b2......A1,nbn=A1,jb1

A 2 , 1 b 1 ⊕ A 2 , 2 b 2 ⊕ . . . . . . A 2 , n b n = A 2 , j b 2 A_{2,1}b_1\oplus A_{2,2}b_2\oplus......A_{2,n}bn=A_{2,j}b_2 A2,1b1A2,2b2......A2,nbn=A2,jb2

A n , 1 b 1 ⊕ A n , 2 b 2 ⊕ . . . . . . A n , n b n = A n , j b n A_{n,1}b_1\oplus A_{n,2}b_2\oplus......A_{n,n}bn=A_{n,j}b_n An,1b1An,2b2......An,nbn=An,jbn

继续化简:

( A 1 , 1 ⊕ C 1 , j ) b 1 ⊕ A 1 , 2 b 2 ⊕ . . . . . . A 1 , n b n = 0 (A_{1,1}\oplus C_{1,j})b_1\oplus A_{1,2}b_2\oplus......A_{1,n}bn=0 (A1,1C1,j)b1A1,2b2......A1,nbn=0

A 2 , 1 b 1 ⊕ ( A 2 , 2 ⊕ C 2 , j ) b 2 ⊕ . . . . . . A 2 , n b n = 0 A_{2,1}b_1\oplus (A_{2,2}\oplus C_{2,j})b_2\oplus......A_{2,n}bn=0 A2,1b1(A2,2C2,j)b2......A2,nbn=0

A n , 1 b 1 ⊕ A n , 2 b 2 ⊕ . . . . . . ( A n , n ⊕ C n , j ) b n = 0 A_{n,1}b_1\oplus A_{n,2}b_2\oplus......(A_{n,n}\oplus C_{n,j})b_n=0 An,1b1An,2b2......(An,nCn,j)bn=0

然后用高斯消元求自由元的个数就行了。自由元的个数为 cnt ,该列对最终结果的贡献就是 2 c n t 2^{cnt} 2cnt

乘法原理把每一列乘起来就行了。

代码如下:

#include <bits/stdc++.h>
#define int long long 
#define rep(i,bbb,eee) for(int i=bbb;i<=eee;i++)
#define frep(i,bbb,eee) for(int i=bbb;i>=eee;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define cout(xxx) cout<<fixed<<setprecision(xxx)
#define inf 0x3f3f3f3f
#define pb push_back
#define AC signed
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=1010,M=998244353;

int qmi(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=res*a%M;
		a=a*a%M;
		b>>=1;
	}
	return res%M;
}
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
inline void print(int x)
{   
   if(x<0){putchar('-');x=-x;}
   if(x>9) print(x/10);
   putchar(x%10+'0');
}
//____________________________________________//
int n;
int A[N][N],C[N][N],a[N][N];
int gauss()
{
	int r,c;
	for(r=0,c=0;c<n;c++)
	{
		int t=-1;
		for(int i=r;i<n;i++)
			if(a[i][c])t=i;

		if(t==-1)continue;

		for(int i=c;i<=n;i++)swap(a[t][i],a[r][i]);

		for(int i=r+1;i<n;i++)
			if(a[i][c])
				for(int j=n;j>=c;j--)
					a[i][j]^=a[r][j];

		r++;
	}
	return n-r;
}
void solve()
{
	cin>>n;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			cin>>A[i][j];

	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			cin>>C[i][j];

	int ans=1;
	for(int j=0;j<n;j++)
	{
		for(int i=0;i<n;i++)
			for(int k=0;k<n;k++)
				a[i][k]=A[i][k];

		for(int i=0;i<n;i++)a[i][i]^=C[i][j];
		int t=gauss();
		ans=ans*qmi(2,t)%M;
	}
	cout<<ans<<"\n";
}
AC main()
{
	ios::sync_with_stdio(false);cin.tie(0);
	int _=1;
	//cin>>_;
	while(_--)solve();
	return 0;
}
  1. Tree Constructer 构造+二分图染色

大意:若u的点权为 x ,v 的点权为 y 。 当且仅当 x ∣ y = 2 60 − 1 x|y=2^{60}-1 xy=2601时u,v 之间有条边。点的个数<=100,现给出一个棵树,要求根据树中边的关系构造出一组合法点权。

思路:构造题重点就是就是想到一种简单的构造方式。根据题目,我们要找到是 a-b-c

a ∣ b = b ∣ c = 2 60 − 1 a|b=b|c=2^{60}-1 ab=bc=2601 即 a、c 是一列,b是另一列,类似于染色,黑白色交替。

如果我们令点数目较少的一类为白色点的话,白色点的数目最大不超过50。要保证白色点的之间或运算不为 2 60 − 1 2^{60}-1 2601, 我们可以令白色点最高0,且第 i 号白色点的 第 i 位为0,这样确保白色点之间是合法的,对于黑色点,最高位是1,周围所有的白色点的编号与之对应的位是1。这样黑白之间也是合法的。因为白色点不超过50个,除了最高位的1,其他的是1的位最高是50,那么更高的位必然全部都是0,黑色点之间也是合法的。

代码如下:

#include <bits/stdc++.h>
#define int long long 
#define rep(i,bbb,eee) for(int i=bbb;i<=eee;i++)
#define frep(i,bbb,eee) for(int i=bbb;i>=eee;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define cout(xxx) cout<<fixed<<setprecision(xxx)
#define inf 0x3f3f3f3f
#define pb push_back
#define AC signed
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=110,M=998244353;

int qmi(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=res*a%M;
		a=a*a%M;
		b>>=1;
	}
	return res%M;
}
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
inline void print(int x)
{   
   if(x<0){putchar('-');x=-x;}
   if(x>9) print(x/10);
   putchar(x%10+'0');
}
//____________________________________________//
int n,ans[N],id[N];
vector<int> g[N],w,b;
void dfs(int u,int fa,int c)
{
	if(c)w.pb(u);
	else b.pb(u);
	for(auto v:g[u])
	{
		if(v==fa)continue;
		dfs(v,u,c^1);
	}
}
void solve()
{
	cin>>n;
	rep(i,1,n)id[i]=-1;
	rep(i,1,n-1)
	{
		int x,y;
		cin>>x>>y;
		g[x].pb(y);
		g[y].pb(x);
	}
	dfs(1,0,0);
	if(w.size()>b.size())swap(w,b);
	int t=(1LL<<59)-1;
	for(int i=0;i<w.size();i++)
	{
		int u=w[i];
		id[u]=i;		
		ans[u]=t^(1LL<<i);
	}
	t=1LL<<59;
	for(int i=0;i<b.size();i++)
	{
		int u=b[i];
        ans[u]=t;
		for(auto v:g[u])
		{
        
			if(id[v]!=-1)
			{
				ans[u]|=(1LL<<id[v]);
			}
		}
	}
	rep(i,1,n)cout<<ans[i]<<" ";
}
AC main()
{
	ios::sync_with_stdio(false);cin.tie(0);
	int _=1;
	//cin>>_;
	while(_--)solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值