其他分享
首页 > 其他分享> > 20220723

20220723

作者:互联网

fft

solve(l,r) 只与区间长度有关,可以改写成始终 \(l=0\)

打表可知每次取最大的 \(2^{k}<n\) 作为分端点最优。递推出 \(f[0]=1,f[i]=2f[i-1]+i2^{i}\) 表示 \([0,2^{i})\) 的答案,每次枚举二进制中的 \(1\),答案为 \(\sum_{\text{n的第i位是1}}(i+1)2^{i+1}+f[i]-(ctz(n)+1)2^{ctz(n)+1}\)

赛时做法是矩乘计算一个区间的答案,动态开点线段树配合光速幂可以做到 \(O(6^{3}\sqrt{r}+6^{2}n\log r)\),和 \(O(n^{2})\) 一个分(有无人教教如何减小矩阵大小)

归纳一下可以得到 \(f[i]=2^{i}+\frac{i(i+1)}{2}2^{i}\),答案为 \(\sum_{\text{n的第i位是1}}2^{i-1}(i^{2}+5i+6)-(ctz(n)+1)2^{ctz(n)+1}\)。这是一个二阶等差乘等比的形式,可以 \(O(1)\) 计算

修改操作相当于区间 \(0,1\) 覆盖,使用 ODT 维护 \(0,1\) 段,变化时更新全局 \(ans\) 即可

code
int n,m;
mint ans,pw1[1<<15|3],pw2[1<<15|3];

mint sum(int n) {
	if( n < 0 ) return 0;
	switch(n) {
	case 0: return 3;
	case 1: return 15;
	case 2: return 55;
	default:
        mint pw = pw1[n&32767] * pw2[n>>15];
        return 3 + pw*((LL)n*n+5ll*n+6)-12 + pw*(-2*n-4)+16 + pw*2-8;
	}
}

struct Node {
	int l,r; bool x;
	Node(int l=0,int r=0,bool x=0):l(l),r(r),x(x){}
	bool operator < (const Node &a) const { return l < a.l; }
}; set<Node> s;
set<Node>::iterator split(int p) {
	auto it = s.lower_bound(Node(p));
	if( it != s.end() && it->l == p ) return it;
	--it;
	auto u = *it; s.erase(it);
	return s.emplace(u.l,p-1,u.x), s.emplace(p,u.r,u.x).fi;
}
void cov(int l,int r,bool x) {
	if( l > r ) return;
	auto itr = split(r+1), itl = split(l);
	for(auto i = itl; i != itr; i = s.erase(i))
		if( i->x ) ans -= sum(i->r)-sum(i->l-1);
	s.emplace(l,r,x);
	if( x ) ans += sum(r)-sum(l-1);
}
int bs(int l,bool x) {
	auto i = split(l); int r = l-1;
	while( i->x != x ) r = i->r, i = s.erase(i);
	if( l <= r ) s.emplace(l,r,!x);
	return i->l;
}

signed main() {
#ifdef FS
	freopen("fft1.in","r",stdin);// freopen(".out","w",stdout);
#else
	freopen("fft.in","r",stdin); freopen("fft.out","w",stdout);
#endif
	ios::sync_with_stdio(0);cout.tie(0);
	s.emplace(0,2e9,0);
	pw1[0] = pw2[0] = 1; For(i,1,1<<15) pw1[i] = pw1[i-1] * 2;
	Rep(i,1,1<<15) pw2[i] = pw2[i-1] * pw1[1<<15];
	io>>n>>m; For(i,1,n, l,r) io>>l>>r, cov(l,r,1);
	while( m-- ) {
		int op,x,p; io>>op>>x;
		if( !op ) p = bs(x,0), cov(p,p,1), cov(x,p-1,0);
		else p = bs(x,1), cov(p,p,0), cov(x,p-1,1);
		int i = bs(0,1)+1;
		cout<<(ans-i*Pow(2,i)).x<<endl;
	}
	return 0;
}

sort

使用 \(x=\sum_{i=1}^{x}1\) 的技巧,枚举 \(i\in[0,n)\),把 \(<i\) 的记做 \(0\),\(\ge i\) 的记做 \(1\),这 \(n\) 个串的和就是原串

状态数变为了 \(2^n\),考虑状压 DP,逐位进行转移(\(f[i,s]\) 表示进行了前 \(i\) 个位置的交换,状态为 \(s\) 的概率)。对初始的 \(n\) 个串同时 DP,复杂度就是 \(O(mn2^{n})\)

code
const int N = 15;
int n,m,U,a[N];
mint f[1<<N],g[1<<N];

signed main() { freopen("sort.in","r",stdin); freopen("sort.out","w",stdout);
	io>>n>>m, U = 1<<n; Rep(i,0,n) io>>a[i], --a[i];
	Rep(i,0,n) {
		int s=0; Rep(j,0,n) s |= (a[j]>=i) << j;
		f[s] += 1;
	}
	while( m-- ) Rep(i,1,n) {
		memcpy(g,f,sizeof f), memset(f,0,sizeof f);
		For(s,0,U) {
			bool x = s>>i-1&1, y = s>>i&1;
			if( x == y) f[s] += g[s];
			else if( !x && y ) f[s] += g[s] * (1-233), f[s^3<<i-1] += g[s] * 233;
			else if( x && !y ) f[s] += g[s] * 233, f[s^3<<i-1] += g[s] * (1-233);
		}
	}
	Rep(i,0,n) {
		mint ans; For(s,0,U) ans += (s>>i&1) * f[s];
		io<<ans.x<<' ';
	}
	return 0;
}

sssp

使用结构体 Node 保存从两点间 \(k\) 短路,\(a+b\) 相当于以 \(a\) 的终点(也是 \(b\) 的起点)作为中转,合并得到 \(a\) 的起点到 \(b\) 的终点的路径

Node f[i,s,t] 表示从 \(s\) 到 \(t\) 的经过 \(i\) 条边的路径,后两维转移可以写成矩乘形式。对第一维倍增,询问时向量乘矩阵

复杂度瓶颈在于 Node 加法,最终只需要知道 \(s\) 到 \(t\) 的 \(k\) 短路,但最终 \(n\) 个中转点每个计算了 \(k\) 条,考虑优化。计算矩阵 \(c=a\times b\) 时先将 \(b\) 转置,那么 \(c[i,j]\) 由 \(a[i,k],b[j,k]\) 合并得到。四元组(长度,\(k\),\(a[i,k]\) 中排名 \(x\),\(b[j,k]\) 中排名 \(y\)),初始只考虑 \(x=y=0\) 的 \(n\) 个元素,线性建堆,每次取出堆顶后放入 \((x,y+1)\),如果 \(y=0\) 再放入 \((x+1,y)\)(其实看码更容易理解)

最终复杂度 \(O((n^{2}+qn)(n+k\log k)\log a)\)

code
typedef tuple<int,int,int,int> Ti;
const int inf = 0x3f3f3f3f;

const int N = 169;
int n,m,q;
Vi e[N][N];

void mrg(const int (*a)[15],const int (*b)[15],int *c) {
	static Ti h[N]; int siz = 0;
	For(i,1,n) h[siz++] = {a[i][0]+b[i][0],i,0,0};
	make_heap(h,h+siz,greater<>());
	Rep(i,0,15) {
		int val,k,x,y; tie(val,k,x,y) = h[0];
		pop_heap(h,h+siz,greater<>()), --siz;
		if( val >= inf ) { memset(c+i,0x3f,sizeof(int)*(15-i)); return; }
		c[i] = val;
		if( i < 14 ) {
			auto push=[&](int x,int y) {
				h[siz++] = {a[k][x]+b[k][y],k,x,y},
				push_heap(h,h+siz,greater<>());	
			};
			push(x,y+1);
			if( !y ) push(x+1,y);
		}
	}
}

struct Mat {
	int a[N][N][15]; Mat() { memset(a,0x3f,sizeof a); }
	Mat operator * (Mat b) const {
		Mat c;
		For(i,1,n) Rep(j,1,i) swap(b.a[i][j],b.a[j][i]);
		For(i,1,n) For(j,1,n) mrg(a[i],b.a[j],c.a[i][j]);
		return c;
	}
} f[20];
struct Vec {
	int a[N][15]; Vec() { memset(a,0x3f,sizeof a); }
	int* operator [] (int i) { return a[i]; }
	Vec operator * (Mat b) const {
		Vec c;
		For(i,1,n) Rep(j,1,i) swap(b.a[i][j],b.a[j][i]);
		For(j,1,n) mrg(a,b.a[j],c[j]);
		return c;
	}
};

signed main() {
#ifdef FS
	freopen("sssp13.in","r",stdin); freopen("out","w",stdout);
#else
	freopen("sssp.in","r",stdin); freopen("sssp.out","w",stdout);
#endif
	ios::sync_with_stdio(0);cout.tie(0);
	io>>n>>m>>q; For(i,1,m, x,y) io>>x>>y, e[x][y].pb(read());
	For(i,1,n) For(j,1,n) {
		sort(all(e[i][j]));
		Rep(k,0,min(15,sz(e[i][j]))) f[0].a[i][j][k] = e[i][j][k];
	}
	For(i,1,19) f[i] = f[i-1] * f[i-1];
	while( q-- ) {
		int s,t,a,k; io>>s>>t>>a>>k;
		Vec g; g[s][0] = 0;
		For(i,0,19) if( a>>i & 1 ) g = g * f[i];
		int ans = g[t][k-1];
		cout<<(ans<inf?ans:-1)<<endl;
	}
	return 0;
}

标签:Node,const,int,20220723,15,freopen,return
来源: https://www.cnblogs.com/401rk8/p/16546123.html