• [冲刺国赛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("");
    	}
    }
    
  • 相关阅读:
    37 图的存储结构
    hdu 1272 使用set和并查集
    题目数据输入中间读入字符
    第六篇 模块基础
    第十二章 并发编程
    并发编程(四)
    第五篇 函数进阶
    第四篇 函数基础
    并发编程(三)
    并发编程(二)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16331231.html
Copyright © 2020-2023  润新知