[冲刺国赛2022] 模拟赛4

亿些整理

题目描述

给定 \(n\times n\) 的矩阵 \(a_{i,j}\),如果 \(a_{i,j}\not=-1\) 代表二分图的左部点 \(i\) 向右部点 \(j\) 有权值为 \(a_{i,j}\) 的边。

问是否存在一个完美匹配使得边的权值和为 \(k\) 的倍数,只需要判断,不需要构造方案。

\(n,k\leq 100,-1\leq a_{i,j}<k\)

解法

其实这题跟二分图根本就没关系,考虑找出一个排列 \(p\),使得 \(\forall i,a_{i,p_i}\not=-1\),并且 \(\sum a_{i,p_i}\bmod k=0\)

既然是在矩阵上找排列的问题,可以自然地联想到行列式。由于本题需要表示 \(\sum a_{i,p_i}\bmod k\),所以我们用生成函数来表示它,也就是令第 \(i\) 个位置上的多项式为 \(F_{i,j}(x)=x^{a_{i,j}}\)(特别地,如果 \(a_{i,j}=-1\) 那么 \(F_{i,j}(x)=0\)

定义多项式乘法为模 \(x^k\) 意义下的循环卷积,那么我们只需要求出行列式 \(det\)(也是一个 \(k-1\) 次多项式),如果 \([x^0]det>0\) 说明无解。但是由于行列式前面 \((-1)^{\pi(p)}\) 的系数,可能有解的情况会抵消成 \(0\),那么我们在多项式前面添加系数,就可以保证非常高的正确率了。

问题是怎么求出这个行列式?如果暴力多项式操作是 \(O(n^4\log n)\) 的(我考试时候就写的这个,\(\tt T\) 飞了),但是发现可以求出 \(k\) 个点值,然后插值就可以得到 \(det\),那么这 \(k\) 个点就选择单位根,因为它们契合循环卷积的性质。

对于每个 \(k\),都需要预处理出 \(k|(p-1)\) 的大质数 \(p\),并且计算出它们的原根。时间复杂度 \(O(n^4)\),根据单位根反演的知识,并不需要写拉格朗日插值,只需要把所有点值加起来就可以判断 \([x^0]det\) 是否 \(>0\) 了,不过这都不重要。

#include <cstdio>
#include <random>
#include <iostream>
using namespace std;
const int N = 105;
const int M[] = { 0,          0,          1073741827, 1073741827, 1073741833, 1073741831, 1073741827,
                  1073741831, 1073741833, 1073741833, 1073741831, 1073741857, 1073741833, 1073741839,
                  1073741831, 1073741971, 1073741857, 1073742169, 1073741833, 1073741833, 1073742361,
                  1073741971, 1073741857, 1073741891, 1073741833, 1073742851, 1073741839, 1073741833,
                  1073742209, 1073742053, 1073741971, 1073742289, 1073741857, 1073741857, 1073742169,
                  1073741831, 1073741833, 1073742073, 1073741833, 1073741839, 1073742361, 1073742113,
                  1073741971, 1073741993, 1073741857, 1073742391, 1073741891, 1073742073, 1073741857,
                  1073742391, 1073742851, 1073742169, 1073741969, 1073742053, 1073741833, 1073742671,
                  1073742209, 1073741833, 1073742053, 1073741827, 1073742361, 1073747621, 1073742289,
                  1073742391, 1073741953, 1073741891, 1073741857, 1073742403, 1073742169, 1073742259,
                  1073741831, 1073745781, 1073741833, 1073743861, 1073742073, 1073743051, 1073741833,
                  1073742209, 1073741839, 1073742721, 1073742721, 1073741833, 1073742113, 1073745769,
                  1073742517, 1073743291, 1073741993, 1073742169, 1073741857, 1073743883, 1073742391,
                  1073742671, 1073742673, 1073742289, 1073742073, 1073744911, 1073741857, 1073742277,
                  1073742391, 1073742517, 1073743501 };
const int G[] = { 0, 0,  2,  2, 5, 13, 2, 13, 5, 5,  13, 5, 5,  3, 13, 2,  5, 7, 5,  5, 7, 2, 5, 6, 5, 2,
                  3, 5,  3,  2, 2, 37, 5, 5,  7, 13, 5,  5, 5,  3, 7,  5,  2, 3, 5,  6, 6, 5, 5, 6, 2, 7,
                  3, 2,  5,  7, 3, 5,  2, 2,  7, 2,  37, 6, 10, 6, 5,  2,  7, 2, 13, 2, 5, 6, 5, 2, 5, 3,
                  3, 11, 11, 5, 5, 7,  6, 2,  3, 7,  5,  2, 6,  7, 5,  37, 5, 6, 5,  5, 6, 6, 2 };
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,k,ans,MOD,a[N][N],b[N][N],g[N][N];
void add(int &x,int y) {x=(x+y)%MOD;}
int qkpow(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=r*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return r;
}
int det()
{
	int r=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
			if(!g[i][i] && g[j][i])
			{
				r=MOD-r;
				swap(g[i],g[j]);
				break;
			}
		if(!g[i][i]) return 0;
		r=r*g[i][i]%MOD;
		for(int j=i+1;j<=n;j++)
		{
			int t=g[j][i]*qkpow(g[i][i],MOD-2)%MOD;
			for(int k=i;k<=n;k++)
				add(g[j][k],MOD-g[i][k]*t%MOD);
		}
	}
	return r;
}
signed main()
{
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	n=read();k=read();MOD=M[k];
	mt19937 z(114514);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]=read(),b[i][j]=z()%MOD;
	for(int i=0;i<k;i++)
	{
		static int pw[N]={};
		pw[0]=1;pw[1]=qkpow(G[k],(MOD-1)/k*i);
		for(int j=2;j<k;j++) pw[j]=pw[j-1]*pw[1]%MOD;
		for(int x=1;x<=n;x++)
			for(int y=1;y<=n;y++)
				g[x][y]=~a[x][y]?pw[a[x][y]]*b[x][y]%MOD:0;
		add(ans,det());
	}
	if(ans) puts("Yes");
	else puts("No");
}

亿块田

题目描述

维护一个长度为 \(n\) 的序列 \(a\),初始值给定,有 \(m\) 次询问,需要支持这三种操作:

  • 1 l r x,把 \(i\in[l,r]\)\(a_i\) 按位与上 \(x\)
  • 2 l r x,把 \(i\in[l,r]\)\(a_i\) 按位或上 \(x\)
  • 3 l r,询问 \(i\in[l,r]\)\(a_i\) 的最大值。

\(n,m\leq 2\cdot 10^5,a_i,x<2^{20}\)

解法

考虑按位修改,与的效果就是 不修改 \(/\) 将它们全部变成 \(0\);或的效果就是 不修改 \(/\) 将他们全部变成 \(1\)

发现这东西很像颜色段均摊,我们那个 \(\tt set\) 分别维护每一位 \(0/1\) 的连续段,然后就可以转化成区间加问题。

但是 \(\tt set\) 的常数太大了,我们考虑把这两个过程都放在线段树上,也就是对于一个区间,如果与的效果 \(/\) 或的效果对于每个点都是一致的,那么直接打加法标记。这个可以维护区间与和区间或来判断,时间复杂度基于颜色段均摊,就是 \(O(n\log ^2n)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 800005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x>=10) write(x/10);
	putchar(x%10+'0');
}
int n,m,mx[M],fl[M],sa[M],so[M];
void up(int i)
{
	mx[i]=max(mx[i<<1],mx[i<<1|1]);
	sa[i]=sa[i<<1]&sa[i<<1|1];
	so[i]=so[i<<1]|so[i<<1|1];
}
void build(int i,int l,int r)
{
	if(l==r)
	{
		mx[i]=sa[i]=so[i]=read();
		return ;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	up(i);
}
void add(int i,int c)
{
	mx[i]+=c;fl[i]+=c;sa[i]+=c;so[i]+=c;
}
void down(int i)
{
	if(!fl[i]) return ;
	add(i<<1,fl[i]);add(i<<1|1,fl[i]);fl[i]=0;
}
void gand(int i,int l,int r,int L,int R,int x)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R && (sa[i]&(~x))==(so[i]&(~x)))
		{add(i,(sa[i]&x)-sa[i]);return ;}
	int mid=(l+r)>>1;down(i);
	gand(i<<1,l,mid,L,R,x);
	gand(i<<1|1,mid+1,r,L,R,x);
	up(i);
}
void gor(int i,int l,int r,int L,int R,int x)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R && (sa[i]&x)==(so[i]&x))
		{add(i,(sa[i]|x)-sa[i]);return ;}
	int mid=(l+r)>>1;down(i);
	gor(i<<1,l,mid,L,R,x);
	gor(i<<1|1,mid+1,r,L,R,x);
	up(i);
}
int ask(int i,int l,int r,int L,int R)
{
	if(l>R || L>r) return -1e9;
	if(L<=l && r<=R) return mx[i];
	int mid=(l+r)>>1;down(i);
	return max(ask(i<<1,l,mid,L,R),
	ask(i<<1|1,mid+1,r,L,R));
}
signed main()
{
	freopen("farm.in","r",stdin);
	freopen("farm.out","w",stdout);
	n=read();m=read();build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int op=read(),l=read(),r=read();
		if(op==1) gand(1,1,n,l,r,read());
		if(op==2) gor(1,1,n,l,r,read());
		if(op==3) write(ask(1,1,n,l,r)),puts("");
	}
}
posted @ 2022-05-31 17:08  C202044zxy  阅读(318)  评论(0编辑  收藏  举报