• Gym102341【杂题】


    B Bulbasaur

    给定 (n imes k) 的分层图,设 (f(i,j)) 表示第 (i) 层到第 (j) 层的点不交的路径组大小的最大值,求

    [sum_{i=1}^{n-1}sum_{j=2}^nf(i,j) ]

    (nle 4cdot 10^4,kle 9)


    将一个点拆成两个点之间连一条边。

    考虑朴素的 FF 算法,假设当前要计算 (sum f(1,j)),从第 (1) 层的每个点开始暴力增广,走到尽可能靠后的点。

    (i) 增加的时候,把前一层的贡献减掉,然后从第 (i) 层开始增广,限制不能走到比第 (i) 层还前的点。容易发现我们只会从增广路的终点开始增广。总增广长度为 (nk),使用压位方法的增广一次是 (O(k)) 的。总时间复杂度 (O(nk^2))甚至是关于输入规模线性的?

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define MP make_pair
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> pii;
    const int N = 80003, M = 9;
    int n, m, k, sum, val[N], e[N][M], E[N][M], vis[N];
    pii q[N*M], pre[N][M]; char str[M+2]; LL ans;
    void work(int sx, int sy){
    	vis[sx] = 1<<sy; int fr = 0, re = 0, mx = sx;
    	q[re++] = MP(sx, sy); pii res(sx, sy);
    	while(fr < re){
    		int x = q[fr].fi, y = q[fr].se;
    		if((x & 1) && x > res.fi) res = q[fr]; ++ fr;
    		if(x < m){
    			if(mx == x) vis[++mx] = 0;
    			while(true){
    				int tmp = e[x][y] & ~vis[x+1]; if(!tmp) break;
    				int p = __builtin_ctz(tmp); vis[x+1] |= 1<<p;
    				pre[x+1][p] = MP(x, y); q[re++] = MP(x+1, p);
    			}
    		} if(x > sx){
    			while(true){
    				int tmp = E[x-1][y] & ~vis[x-1]; if(!tmp) break;
    				int p = __builtin_ctz(tmp); vis[x-1] |= 1<<p;
    				pre[x-1][p] = MP(x, y); q[re++] = MP(x-1, p);
    			}
    		}
    	} for(int i = sx>>1;i <= (res.fi>>1);++ sum, ++val[++i]);
    	while(res != MP(sx, sy)){
    		int x = pre[res.fi][res.se].fi, y = pre[res.fi][res.se].se;
    		if(res.fi == x+1){e[x][y] ^= 1<<res.se; E[x][res.se] ^= 1<<y;}
    		else {e[res.fi][res.se] ^= 1<<y; E[res.fi][y] ^= 1<<res.se;}
    		res = MP(x, y);
    	}
    }
    int main(){
    	scanf("%d%d", &n, &k); m = (n<<1)-1;
    	for(int i = 1;i < m;++ i)
    		if(i & 1) for(int j = 0;j < k;++ j){
    			scanf("%s", str);
    			for(int l = 0;l < k;++ l)
    				if(str[l] == '1') e[i][j] |= 1<<l;
    		} else for(int j = 0;j < k;++ j) e[i][j] = 1<<j;
    	for(int i = 1;i < n;++ i){
    		for(int j = 0;j < k;++ j)
    			work(max(1, i-1<<1), j);
    		sum -= val[i]; ans += sum;
    	} printf("%lld
    ", ans);
    }
    

    C Cloyster

    这是一道交互题

    给定正整数 (n),交互器有 (n imes n) 的正整数矩阵 (A),保证数两两不同且对于所有非最大值的格子,周围 (8) 个格子中至少有一个比它大。

    你至多可以询问 (3n+210) 次某个位置的值,要求求出最大值的位置。

    (nle 2000)


    又 tm 被教育了

    询问一行,找到最大值 (X),询问 (X) 的周围,找到目前已知最大值的那一边走过去,如果还在同一行说明已经求出最大值。列同理。

    然后就是标准的 KDT 型分治,操作次数 (3n+12log_2 n)

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 2003;
    template<typename T>
    void read(T &x){
    	int ch = getchar(); x = 0;
    	for(;ch < '0' || ch > '9';ch = getchar());
    	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    } template<typename T>
    bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
    int n, a[N][N], x0, y0, mx;
    int qry(int x, int y){
    	if(a[x][y]) return a[x][y];
    	printf("? %d %d
    ", x, y); fflush(stdout);
    	read(a[x][y]); return a[x][y];
    } void work(int u, int d, int l, int r){
    	if(u == d && l == r){printf("! %d %d
    ", u, l); exit(0);}
    	if(d - u < r - l){
    		int m = l+r>>1, p = u, v = qry(u, m);
    		for(int i = u+1;i <= d;++ i)
    			if(chmax(v, qry(i, m))) p = i;
    		if(chmax(mx, v)){x0 = p; y0 = m;}
    		for(int x = max(p-1, 1);x <= p+1 && x <= n;++ x){
    			if(m > 1 && chmax(mx, qry(x, m-1))){x0 = x; y0 = m-1;}
    			if(m < n && chmax(mx, qry(x, m+1))){x0 = x; y0 = m+1;}
    		} if(y0 == m){printf("! %d %d
    ", p, m); exit(0);}
    		if(y0 < m) work(u, d, l, m-1); else work(u, d, m+1, r);
    	} else {
    		int m = u+d>>1, p = l, v = qry(m, l);
    		for(int i = l+1;i <= r;++ i)
    			if(chmax(v, qry(m, i))) p = i;
    		if(chmax(mx, v)){x0 = m; y0 = p;}
    		for(int y = max(p-1, 1);y <= p+1 && y <= n;++ y){
    			if(m > 1 && chmax(mx, qry(m-1, y))){x0 = m-1; y0 = y;}
    			if(m < n && chmax(mx, qry(m+1, y))){x0 = m+1; y0 = y;}
    		} if(x0 == m){printf("! %d %d
    ", m, p); exit(0);}
    		if(x0 < m) work(u, m-1, l, r); else work(m+1, d, l, r);
    	}
    } int main(){read(n); work(1, n, 1, n);}
    

    E Eevee

    给定 (k) 个长为 (n) 的排列 (p_i),设 (f(i,j)) 表示满足所有长为 (ell=j-i+1) 的子段都不全相等的、将 (p_i,p_{i+1},cdots,p_j) 归并的方案数。求

    [left(sum_{1le i<jle k}f(i,j) ight)mod(10^9+7) ]

    (2le k,nle 300,p_i) 在所有长为 (n) 的排列中独立均匀随机生成。


    直接容斥,枚举 ([n]) 的子集 (S),要求 (S)([l,r]) 这一段的出现位置顺序相同,贡献系数是 ((-1)^{|S|}),方案数是一堆可重集排列的乘法原理。

    于是你获得了垃圾的指数级做法

    考虑 dp,首先枚举区间左端点 (l),对于每个二元组 ((u,v)) 记录 (R(u,v)) 表示使得 ([l,R(u,v))) 的排列满足 (u)(v) 先出现的最大值。

    然后枚举 (r:l+1 ightarrow n),动态维护转移系数,每次做一遍 dp。

    (f_u) 表示 (S) 中最后出现的元素是 (u),按照 (p_l) 中元素的出现顺序转移,枚举下一个出现的是 (v)

    (rge R(u,v)) 时没有贡献,所以对于每个 (u),将所有 (v) 按照 (R(u,v)) 降序排序,按照这个顺序转移就可以剪枝。

    要先预处理一下 (nk) 以内的阶乘、阶乘逆元,最后注意把总方案数算上。

    关于时间复杂度,第一部分是 (O(n^2k)),第二部分是 (sum_{l<r}sum_{u,v}(R(u,v)-l)=O(nk^2))

    #include<bits/stdc++.h>
    #define PB emplace_back 
    using namespace std;
    typedef long long LL;
    const int N = 303, M = N*N, mod = 1e9 + 7;
    template<typename T>
    void read(T &x){
    	int ch = getchar(); x = 0;
    	for(;ch < '0' || ch > '9';ch = getchar());
    	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    } template<typename T>
    bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
    int ksm(int a, int b){
    	int res = 1;
    	for(;b;b >>= 1, a = (LL)a * a % mod)
    		if(b & 1) res = (LL)res * a % mod;
    	return res;
    } void qmo(int &x){x += x >> 31 & mod;}
    int k, n, a[N][N], pos[N][N], fac[M], inv[M], R[N][N], dp[N], coe[N][N], sc[N][N], f[N], g[N], sf[N], sg[N], ans;
    vector<int> E[N];
    void init(int m){
    	fac[0] = 1;
    	for(int i = 1;i <= m;++ i) fac[i] = (LL)fac[i-1] * i % mod;
    	inv[m] = ksm(fac[m], mod-2);
    	for(int i = m;i;-- i) inv[i-1] = (LL)inv[i] * i % mod;
    } int C(int n, int m){
    	if(m < 0 || n < m) return 0;
    	return (LL)fac[n] * inv[m] % mod * inv[n-m] % mod;
    }
    int main(){
    	read(k); read(n); init(n*k);
    	for(int i = 0;i < k;++ i)
    		for(int j = 0;j < n;++ j){
    			read(a[i][j]);
    			pos[i][--a[i][j]] = j;
    		}
    	for(int l = 0;l < k-1;++ l){
    		for(int u = 0;u < n;++ u){
    			E[u].resize(0); f[u] = g[u] = 1;
    			sf[u] = pos[l][u]; sg[u] = n-pos[l][u]-1;
    			for(int v = 0;v < n;++ v)
    				if(pos[l][u] < pos[l][v] && pos[l+1][u] < pos[l+1][v]){
    					chmax(R[u][v], l+2);
    					while(R[u][v] < k && pos[R[u][v]][u] < pos[R[u][v]][v]) ++ R[u][v];
    					E[u].PB(v); coe[u][v] = 1; sc[u][v] = pos[l][v]-pos[l][u]-1;
    				}
    			sort(E[u].begin(), E[u].end(), [&](int x, int y){return R[u][x] > R[u][y];});
    		} for(int r = l+1;r < k;++ r){
    			memset(dp, 0, sizeof dp);
    			for(int _ = 0;_ < n;++ _){
    				int u = a[l][_]; sf[u] += pos[r][u]; sg[u] += n-pos[r][u]-1;
    				f[u] = (r-l+1ll) * C(sf[u], pos[r][u]) % mod * f[u] % mod;
    				g[u] = (LL)C(sg[u], n-pos[r][u]-1) * g[u] % mod;
    				qmo(dp[u] -= f[u]); ans = (ans + (LL)dp[u] * g[u]) % mod;
    				for(int v : E[u]){
    					if(r >= R[u][v]) break;
    					sc[u][v] += pos[r][v]-pos[r][u]-1;
    					coe[u][v] = (r-l+1ll) * C(sc[u][v], pos[r][v]-pos[r][u]-1) % mod * coe[u][v] % mod;
    					qmo(dp[v] -= (LL)dp[u] * coe[u][v] % mod);
    				}
    			}
    		}
    	} for(int i = 2, pw = inv[n];i <= k;++ i){
    		pw = (LL)pw * inv[n] % mod;
    		ans = (ans + (k-i+1ll)*pw%mod*fac[i*n])%mod;
    	} printf("%d
    ", ans);
    }
    

    I Infernape

    给定 (n) 个点的树,(q) 次询问 (k) 个圆 (C(v,r)),求被至少 (k-1) 个圆包含的点的个数。

    (nle 10^5,sum kle 3cdot 10^5)


    计算圆的交集之前做过了,然后就成 sb 题了(

    不同的是计算圆面积包含的点数需要用离线+点分治/点分树,时间复杂度 (O((n+sum k)log n))

    L Lati@s

    Nimber 积和式。

    (nle 150,M_{i,j}in[0,2^{64}))


    Nimber 积和式跟行列式完全一样,所以算行列式就完事了(

    Nimber 在 ([0,2^{2^n})) 上构成有限域,(x) 的逆元即为 (x^{2^{2^n}-2})

    复习一下 Nim 积:设 (x=acdot 2^{2^{m-1}}+b,y=ccdot 2^{2^{m-1}}+d),其中 (a,b,c,din[0,2^{2^{m-1}})),则有

    [egin{aligned} xotimes y&=(aotimes 2^{2^{m-1}}oplus b)otimes(cotimes 2^{2^{m-1}}oplus d) \ &=aotimes cotimes 3cdot 2^{2^{m-1}-1}oplus (aotimes doplus botimes c)otimes 2^{2^{m-1}}oplus botimes d \ &=aotimes cotimes 2^{2^{m-1}-1}oplus(aotimes coplus aotimes doplus botimes c)cdot 2^{2^{m-1}}oplus botimes d \ &=aotimes cotimes 2^{2^{m-1}-1}oplus((aoplus b)otimes(coplus d)oplus botimes d)cdot 2^{2^{m-1}}oplus botimes d end{aligned} ]

    然后 ([0,2^{16})) 内的原根是 (258),预处理离散对数之后可以直接算。(64) 位的 Nim 积计算大概是异或运算的 (10sim 20) 倍常数,可以近似地看成 (O(1))

    行列式直接高斯消元就完事了。时间复杂度 (O(n^3))

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned u32;
    typedef unsigned short u16;
    typedef unsigned long long u64;
    const u32 r = 258;
    const int N = 1<<16;
    template<typename T>
    void read(T &x){
    	int ch = getchar(); x = 0;
    	for(;ch < '0' || ch > '9';ch = getchar());
    	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    } u16 pw[N], pos[N];
    u16 mulp(u16 x, u16 y, u16 n = 16){
    	if(x <= 1 || y <= 1) return x * y;
    	u16 a = x>>n, b = x&(1<<n)-1, c = y>>n, d = y&(1<<n)-1, bd = mulp(b, d, n>>1);
    	return ((mulp(a^b, c^d, n>>1)^bd)<<n)^bd^mulp(mulp(a, c, n>>1), 1<<n-1, n>>1);
    } u16 mul16(u16 x, u16 y){
    	if(x <= 1 || y <= 1) return x * y;
    	int tmp = pos[x]+pos[y];
    	return pw[tmp >= 65535 ? tmp - 65535 : tmp];
    } u32 mul32(u32 x, u32 y){
    	u16 a = x>>16, b = x&65535, c = y>>16, d = y&65535, bd = mul16(b, d);
    	return ((mul16(a^b, c^d)^bd)<<16)^bd^mul16(mul16(a, c), 32768);
    } u64 mul64(u64 x, u64 y){
    	u32 a = x>>32, b = x&u32(-1), c = y>>32, d = y&u32(-1), bd = mul32(b, d);
    	return (u64(mul32(a^b, c^d)^bd)<<32)^bd^mul32(mul32(a, c), 1u<<31);
    } int n; u64 A[150][150], res = 1;
    u64 ksm(u64 a, u64 b){
    	u64 res = 1;
    	while(b){
    		if(b & 1) res = mul64(res, a);
    		a = mul64(a, a); b >>= 1;
    	} return res;
    } u64 inv(u64 a){return ksm(a, u64(-2));}
    int main(){
    	for(u16 i = 0, x = 1;i < 65535;++ i, x = mulp(x, r)){
    		pw[i] = x; pos[x] = i;
    	} read(n);
    	for(int i = 0;i < n;++ i)
    		for(int j = 0;j < n;++ j)
    			read(A[i][j]);
    	for(int i = 0;i < n;++ i){
    		int p = i; while(p < n && !A[p][i]) ++ p;
    		if(p == n) return puts("Second"), 0;
    		swap(A[i], A[p]);
    		u64 tmp = inv(A[i][i]);
    		res = mul64(res, A[i][i]);
    		for(int j = i;j < n;++ j)
    			A[i][j] = mul64(A[i][j], tmp);
    		for(int j = i+1;j < n;++ j)
    			for(int k = i+1;k < n;++ k)
    				A[j][k] ^= mul64(A[j][i], A[i][k]); 
    	} puts(res ? "First" : "Second");
    }
    
  • 相关阅读:
    12 EF Core 私有字段的映射
    11 EF Core 表拆分
    10 EF Core 继承类关系映射
    9. EF Core数据库索引与备用键约束
    8. EF Core 外键的删除模式
    7. EF Core 导航属性配置
    C# 单例模式
    JS中将XML转为JSON对象
    MVC特性
    测试sql语句执行速度
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/14629050.html
Copyright © 2020-2023  润新知