[SDOI2016]储能表

280 篇文章 1 订阅

题目

传送门 to luogu

传送门 to usOJ

思路

看了看大多数题解,似乎都是神仙的数位 d p dp dp

本蒟蒻实在是没办法往那个方面想,所以给出了一种更加 令人迷惑 美好的做法。

声明:下文中的区间都只涉及整数。例如, [ a , b ] [a,b] [a,b] 实际指 [ a , b ] ∪ Z [a,b]\cup\Z [a,b]Z

简单的小情况

如果 n = 2 k n=2^k n=2k ,是否有非常舒服的做法呢?

给出这样一个定理: ∀ x ∈ [ 0 , 2 k ) , { x ⊕ r ∣ r ∈ [ 0 , 2 k ) } = [ 0 , 2 k ) \forall x\in[0,2^k),\{x\oplus r|r\in[0,2^k)\}=[0,2^k) x[0,2k),{xrr[0,2k)}=[0,2k)

用大白话来说,一个小于 2 k 2^k 2k 的数,异或上 2 k − 1 2^k-1 2k1 以内的所有数,仍然得到这些数。

证明是极其简易的:异或的结果小于 2 k 2^k 2k ,且互不相同,只好是遍布 [ 0 , 2 k ) [0,2^k) [0,2k)

所以,如果 n = 2 k n=2^k n=2k ,可以直接发现,表中的所有值就是( a × b a\times b a×b 表示出现了 a a a b b b ):

m × 0 + m × 1 + m × 2 + ⋯ + m × ( 2 k − 1 ) m\times 0+m\times 1+m\times 2+\cdots+m\times (2^k-1) m×0+m×1+m×2++m×(2k1)

复杂的小局面

如果 n ≠ 2 k n\ne 2^k n=2k ,这能够难倒一直受虐、死者之心的小蒟蒻吗?

可以考虑将 n n n 拆分一下,按照最高位分一个类。或者说,把这 n n n 个数字,像在 0 / 1    T r i e 0/1\;Trie 0/1Trie 树上一样,放在左右子树中。

m m m 当然也可以这么做。然后绝招来了:两两匹配,四个递归!(还挺押韵的

显然,两两匹配之后,结果是正确的。也就是说,将 [ 0 , n ) [0,n) [0,n) 分成了 A , B A,B A,B m m m 则是 C , D C,D C,D ,那么最后的答案,可以转化为 ⟨ A , C ⟩ , ⟨ A , D ⟩ , ⟨ B , C ⟩ , ⟨ B , D ⟩ \langle A,C\rangle,\langle A,D\rangle,\langle B,C\rangle,\langle B,D\rangle A,C,A,D,B,C,B,D 四个相似的问题。

那么,我们选择最大的 k k k ,满足 2 k < n 2^k<n 2k<n ,然后切开!

于是,最高位的异或值就不归它管了。这种在 0 / 1    T r i e 0/1\;Trie 0/1Trie 树上的行走,当然是有这样性质的。

直接把高位已经得到的异或值丢进递归里面就可以了。

综合的中和

我们已经有了思路,用 f ( x , n , m ) f(x,n,m) f(x,n,m) 表示,已经得到的异或值是 x x x [ 0 , n ) ⊕ [ 0 , m ) [0,n)\oplus[0,m) [0,n)[0,m) 作出的贡献( k k k 是需要被考虑的,作为全局变量)。

不妨设 n ≥ m n\ge m nm 。如果 n = 2 k n=2^k n=2k ,那么可以直接计算出答案:因为得到的结果一定是 m × ( 0 + x ) , m × ( 1 + x ) , m × ( 2 + x ) , ⋯   , m × ( n − 1 + x ) m\times(0+x),m\times (1+x),m\times(2+x),\cdots,m\times(n-1+x) m×(0+x),m×(1+x),m×(2+x),,m×(n1+x)

如果 2 k < n < 2 k + 1 2^k<n<2^{k+1} 2k<n<2k+1 ,那么将 [ 0 , n ) [0,n) [0,n) 分解为 [ 0 , 2 k ) , [ 2 k , n ) [0,2^k),[2^k,n) [0,2k),[2k,n) ,再令 r = min ⁡ ( 2 k , m ) r=\min(2^k,m) r=min(2k,m) ,将 [ 0 , m ) [0,m) [0,m) 分解为 [ 0 , r ) , [ r , m ) [0,r),[r,m) [0,r),[r,m) ,两两组合。在组合的时候,注意要给 x x x 加上一个 2 k 2^k 2k ,并且将 [ 2 k , n ) [2^k,n) [2k,n) 这种超过了 2 k 2^k 2k 的区间减掉一个 2 k 2^k 2k ——毕竟这个异或值已经累加到 x x x 里了。

可是复杂度呢?这不是一个 T ( n ) = 4 T ( n 2 ) + O ( 1 ) \mathcal T(n)=4\mathcal T(\frac{n}{2})+\mathcal O(1) T(n)=4T(2n)+O(1) 吗?

不是这样的。拆解一次,会拆出来 2 k 2^k 2k ,就变成 O ( 1 ) \mathcal O(1) O(1) 的了。

所以,我们每次只会实际递归一次,复杂度仍然是 O ( log ⁡ n ) \mathcal O(\log n) O(logn) 的。

代码

  • 计算等差数列要除以二,所以只能取模 2 P 2P 2P
  • m = 0 m=0 m=0 只会是因为递归之前, n ≤ r ∨ m ≤ r n\le r\vee m\le r nrmr,没有实际意义,只能使代码变短
  • i i i 相当于 2 k 2^k 2k 中的 k k k ,亦可以理解为 0 / 1    T r i e 0/1\;Trie 0/1Trie 上面的行走。
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
inline long long readint(){
	long long a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(long long x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar((x%10)^48);
}

# define MB template < typename T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }

const int LogN = 60;
typedef long long int_;
int P; int_ k;

int_ ans; // 答案
void solve(int_ now,int_ n,int_ m,int i=LogN){
	const int_ r = 1ll<<i>>1, all = (1ll<<i)-1;
	if(now+all <= k) return ; // 一定小于k
	if(n < m) swap(n,m); // 保持 n 大 m 小
	if(m <= 0) return ; // 不可能的情况!
	if(n == all+1){ // 简单的小情况
		if(now < k) // 有一些是不能用的
			n -= k-now, now = k;
		m %= P, n %= P<<1;
		ans = ((n*(n-1)>>1)%P*m+ans)%P;
		ans = ((now-k)%P*m%P*n+ans)%P;
		return ;
	}
	solve(now,n-r,m-r,i-1);
	solve(now^r,n-r,min(m,r),i-1);
	solve(now^r,min(n,r),m-r,i-1);
	solve(now,min(n,r),min(m,r),i-1);
}

int main(){
	int_ n, m;
	for(int T=readint(); T; --T){
		n = readint(), m = readint();
		k = readint(), P = readint();
		ans = 0, solve(0,n,m);
		printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值