Problem Set.7

CodeChef - CHEFPRAD Chef and Pairs

https://www.cnblogs.com/zxhl/p/7017988.html

image

等价于从 \(S\) 中选择 \(n\) 个数字,填到前 \(n\) 位或者所有奇数位的答案

\(f_{i,j}\) 表示考虑前 \(i\) 个空,和为 \(j\) 的方案数

则答案就是 $$ans=\Sigma_{i=0}^{9 \times n} ,2\times (f_{n,i})^2$$

但是这样可能算重,即同时满足前 \(n\) 位和等于后 \(n\) 位和,和奇数位和等于偶数位和

这样的方案数是,$$(\Sigma_{i=0}^{9\times n} (f_{n/2,i})^2) \times (\Sigma_{i=0}^{9\times n} (f_{n-n/2,i})^2)$$

不妨设 \(n=3\) 考虑

\[a_1+a_2+a_3=a_4+a_5+a_6 \]

\[a_1+a_3+a_5=a_2+a_4+a_6 \]

\[a_5-a_2=a_2-a_5 \]

\[a_2=a_5 \]

\(n=5\) 的就可以发现

\[\Sigma_{i=1}^{n}\,a_i=\Sigma_{i=1}^n a_{i+n}[i\land 1] \]

同时还要满足 \(i\) 为偶数的情况,这就是选 \(n/2\) 个填到奇数里面和偶数里面

这两个独立,所以是 \(\times\)


CF232B

我们发现 \(i\)\(i+n\) 的情况是等价的,我们只用考虑 \(n^2\) 的矩阵即可

根据每一行选 \(j\) 个来划分集合

\[f_{i,k+j} \leftarrow \Sigma_{j=0}^{n} \,\Sigma_{k=0}^{min(i \times n-j,q-j)} f_{i-1,k} \times {n \choose j}^{n/m+[i \leq\,n\,mod\,m]} \]

快速幂预处理这个次幂,\(O(n^4)\)

计数题能预处理就预处理

const int N=110,M=1e4+10,Q=1e9+7;
int n,m,q;
int C[N][N],f[N][M],val[N][M];

int qkw(int a,int k)
{
	int ans=1,base=a;
	while(k)
	{
		if(k&1) ans=ans*base%Q;
		base=base*base%Q;
		k>>=1;
	}
	return ans;
}

void prework()
{
	for(int i=0;i<=n;i++)
		for(int j=0;j<=i;j++)
		{
			if(!j) C[i][j]=1;
			else C[i][j]=(C[i-1][j-1]+C[i-1][j])%Q;
		}
	
	for(int j=0;j<=n;j++)
	{
		int k1=qkw(C[n][j],m/n+1);
		int k2=qkw(C[n][j],m/n);
		for(int i=1;i<=n;i++)
			val[i][j]=(i<=m%n)?k1:k2;
	}
}

signed main()
{
	n=fr(),m=fr(),q=fr();
	prework();
	
	f[0][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=n;j++)
			for(int k=0;k+j<=min(i*n,q);k++)
				(f[i][k+j]+=f[i-1][k]*val[i][j]%Q)%=Q;
	fw(f[n][q]);
	return 0;
}

骑士与国王

image

\(f_i\) 表示恰好前 \(i\) 个俱乐部叛乱的方案数

初始 \(f_i=s_i!\times (n-s_i)!\)

但是这样没法保证恰好 \(i\) 个,即使强制让 \(i+1\) 不在位置上也不行,因为后面还是可以构造出非 \(i\) 的方案

考虑去重

我们就假设之前的一定是恰好 \(i\),我们就只用考虑当前的 \(i\) 可以归属到之前的某个 \(j\) 这样就去掉了,即现在为合法方案中满足 \(\Sigma k_p \in [n,n-\Sigma k_p+1]\) 中最小的 \(p\) 把它算入 \(p\) 的贡献

直接枚举之前的某个 \(j\),则 \(i\) 需要减去 \(f_j \times (s_i-s_j)!\),表示恰好这样摆同时满足 \(j,i\)

然后我们不乘 \((n-s_i)!\),方便转移,最后答案累加一下即可

const int N=1e6+10,Q=1e9+7;
int n,m;
int k[N],fac[N],f[N],s[N];

signed main()
{
	n=fr(),m=fr();
	fac[0]=1ll;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%Q;
	for(int i=1;i<=m;i++) k[i]=fr();
	for(int i=1;i<=m;i++) s[i]=s[i-1]+k[i];

	for(int i=1;i<=m;i++)
	{
		f[i]=fac[s[i]];
		for(int j=1;j<i;j++)
		{
			f[i]-=f[j]*fac[s[i]-s[j]]%Q;
			(f[i]+=Q)%=Q;
		}
	}
	
	int ans=0;
	for(int i=1;i<=m;i++) (ans+=f[i]*fac[n-s[i]]%Q)%=Q;
	fw((fac[n]-ans+Q)%Q);
	return 0;
}

给定 \(n\) 个篮子,每个篮子有三个数 \(a_i,b_i,c_i\),每个篮子选一个,放入 \(A\,B\,C\) 三个集合中,问 \(\min A_{max}+B_{max}+C_{max}\)

一个类三维偏序问题

考虑对第一维从大到小排序,枚举当前最大值 \(a_{max}\),对于所有 \(a_i \leq a_{max}\) 的物品,显然分到 \(A\) 集合

等价于只用考虑 \(a_i \geq a_{max}\) 的物品,等价于每次加入一个物品

考虑 \(B\) 集合的最大值,对于每一个可能的最大值 \(b_{max}\),我们维护当前加入物品中所有 \(b_i \geq b_{max}\)\(c_{max}\),同时维护 \(b+c\) 的最小值即可

考虑到 \(b_i\) 的值域属性,放到值域线段树上维护,加一个物品区间 \(max\) 即可


P3869 [TJOI2009] 宝藏

赛时没有做出来是因为想直接状压开关然后把整个图还原,这样就太麻烦了

这个题的本质还是一个搜索,所以状压只是辅助状态(搜索状态中的一维)

每次更新就扩散,然后便利状态,注意如果当前点是开关,一定是把状态 \(\oplus\) 不是 \(\mid\),因为可能需要先踩一个开关开条新路去开另一个开关,然后去终点的最短路上恰好需要再次关掉它,即一个开关不是要么不开,要么开一次

const int N=35,M=1<<10;
int n,m,k,sx,sy,ex,ey;
int r[N],c[N],R[N],C[N],d[N][N][M];
char a[N][N];
struct node{int x,y,S;};

int bfs()
{
	queue<node> q;
	memset(d,-1,sizeof d);
	d[sx][sy][0]=0;
	q.push({sx,sy,0});
	
	while(q.size())
	{
		auto t=q.front();
		q.pop();
		
		if(t.x==ex && t.y==ey) return d[ex][ey][t.S];
		for(int i=0;i<4;i++)
		{
			int xx=t.x+dx[i],yy=t.y+dy[i];
			if(xx>=1 && xx<=n && yy>=1 && yy<=m)
			{
				int fl=(a[xx][yy]!='#'),T=t.S; //注意!='#' 还有 'S' 和 'T'
				for(int j=0;j<k;j++)
				{
					if(xx==r[j] && yy==c[j]) T^=(1<<j); //这里一定要写 ^ 可能一个点走多次
					if(xx==R[j] && yy==C[j] && ((t.S>>j)&1)) fl^=1;
				}
				if(fl && !(~d[xx][yy][T]))
				{
					q.push({xx,yy,T});
					d[xx][yy][T]=d[t.x][t.y][t.S]+1;
				}
			}
		}
	}
	return -1;
}

int main()
{
	n=fr(),m=fr();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			cin>>a[i][j];
			if(a[i][j]=='S') sx=i,sy=j;
			if(a[i][j]=='T') ex=i,ey=j;
		}
	k=fr();
	for(int i=0;i<k;i++)
		r[i]=fr(),c[i]=fr(),R[i]=fr(),C[i]=fr();
	fw(bfs());
	return 0;
}

P3475 [POI2008] POD-Subdivision of Kingdom

假设两个集合分别为 \(S,T\)

\(dfs\) 枚举每个数是加入 \(S/T\),然后计算贡献,最优性剪枝+\(bitset\) 优化

由于我们限制了 \(|S|=|T|=\frac{n}{2}\) 所以是 \(O({n \choose \frac{n}{2}})\)

这里限制集合大小就是判断当前的两个集合 \(size\) 如果没到 \(\frac{n}{2}\) 就可以加,每条非同集合的边都在编号较大的那个点被算到,具体可以看代码

const int N=32;
int n,m,k,u,v,ANS=INT_MAX;
int ans[N];
bitset<32> g[N],M;
vector<int> a;

void dfs(int x,bitset<32> S,bitset<32> T,int val)
{
	if(val>=ANS) return;
	if(x>=n+1)
	{
		ANS=val;
		for(int i=1;i<=k;i++)
			ans[i]=a[i-1];
		return;
	}

	if(S.count()<k)
	{
		a.pb(x);
		int res=(T&g[x]).count();
		M=S,M[x]=1;
		dfs(x+1,M,T,val+res);
		a.pop_back();
	}
	
	if(T.count()<k)
	{
		int res=(S&g[x]).count();
		M=T,M[x]=1;
		dfs(x+1,S,M,val+res);
	}
}

int main()
{
	n=fr(),m=fr(),k=n>>1;
	for(int i=1;i<=m;i++)
	{
		u=fr(),v=fr();
		g[u][v]=g[v][u]=1;
	}
	
	dfs(1,M,M,0);
	for(int i=1;i<=k;i++) fw(ans[i]),pt;
	return 0;
}

当然大力退火即可

const int N=32;
int n,m,k,u,v,ANS;
bitset<32> g[N];
int ans[N],res[N];
mt19937 Rand(99994919);

double ner(double l,double r)
{
    return (double)Rand()/UINT_MAX*(r-l)+l;
}

int calc(int a[])
{
	int sum=0;
	bitset<32> M;
	M.reset();
	for(int i=1;i<=k;i++) M[a[i]]=1;
	for(int i=1;i<=k;i++)
		sum+=((g[a[i]]|M)^M).count();
	return sum;
}

void SA()
{
	for(double T=1e6;T>=1e-12;T*=0.999864)
	{
		int x=rand()%k+1,y=rand()%k+1+k;
		swap(res[x],res[y]);
		int RES=calc(res);
		double dt=RES-ANS;
		if(dt<0) memcpy(ans,res,sizeof res),ANS=RES;
		else if(exp(-dt/T)>ner(0,1)) swap(res[x],res[y]);
	}
}

int main()
{
	srand(time(0));
	n=fr(),m=fr(),k=n>>1;
	for(int i=1;i<=m;i++)
	{
		u=fr(),v=fr();
		g[u][v]=g[v][u]=1;
	}
	for(int i=1;i<=n;i++) ans[i]=res[i]=i;
	ANS=calc(ans);
	while((double)clock()/CLOCKS_PER_SEC<0.9) SA();
	
	sort(ans+1,ans+1+k);
	for(int i=1;i<=k;i++) fw(ans[i]),pt;
	return 0;
}

bzoj 4972

给定网格图和 \(a_{i,j}\)\(q\) 次询问,给定 \((x,y,k)\)\((x-k,y),(x,y-k),(x,y)\) 三个点组成的三角形的点权和

\(n,m \leq 3000\)\(q \leq 3e6\)

image

对于第一列和第一行都进行斜线前缀和+整个正常前缀和即可


给定序列,求满足区间中出现的数都出现偶数次的区间个数

\(n \leq 5e6\)

每个数随机赋值一个 \(64\) 位整数,做一遍区间 \(\oplus\) 前缀和,判断 \(s[l,r]=0\) 的区间个数即可


礼物

小诗收到了来自粉丝的 \(n\) 个礼物,编号 \(1,2,\dots,n\)

她想将这些礼物分成两组,并给予分组情况一个评分

具体地,评分值为有序礼物编号对 \((a,b)\) 的数量,满足 \(b\)\(a\) 的倍数(即 \(b\bmod a=0\)),且 \(a\) 分到第一组,\(b\) 分到第二组

她想知道,第一组的大小为 \(0,1,2,\cdots,n-1\) 时,评分的最大值分别是多少

输入

一行,一个正整数 \(n\)

输出

一行,\(n\) 个非负整数,第 \(i\) 个表示第一组大小为 \(i-1\) 时评分的最大值

4
0 3 3 2
10
0 9 12 13 13 12 11 9 6 3
100
0 99 147 178 200 218 230 242 250 258 265 271 276 279 282 285 287 289 291 292 293 294 295 295 295 295 295 295 295 294 293 292 291 290 289 288 287 286 285 284 283 282 281 280 279 277 275 273 271 269 266 263 260 257 254 251 248 245 242 239 236 233 230 227 224 221 218 215 212 208 204 200 196 192 187 182 177 172 167 162 157 152 147 141 135 129 122 115 108 101 94 87 80 72 64 55 44 33 22 11

\(\varnothing,\{1,2,3,4\}\),评分为 \(0\)

\(\{1\},\{2,3,4\}\),评分为 \(3\)

\(\{1,2\},\{3,4\}\),评分为 \(3\)

\(\{1,2,3\},\{4\}\),评分为 \(2\)

\(2\leqslant n\leqslant 3\times 10^5\)

显然有暴力的 \(O(n\log^2 n)\) 的线段树做法,初始化所有点在集合 \(b\),动态维护每个点放到 \(a\) 集合的贡献即可

每次加入一个数需要修改 \(O(\log n)\) 个节点,每次修改 \(O(\log n)\)

考虑优化这个算法,考虑贪心

注意到对于 \(b\bmod a=0\),将 \(a\) 分入第一组,\(b\) 分入第二组,容易发现评分值不减

考虑初始令所有数在第二组内,依次将数字添加入第一组内

对于新选的数,选择时其约数一定未被选,其倍数一定全被选,因此对评分的贡献确定,等于其约数个数减去倍数个数

考虑上述线段树的维护过程,就是每次选贡献最大点,而这里贪心选择的点也是贡献最大点

同时我们按照贡献排序,由于 \(i\) 的约数 \(j\) 至少多 \(i\) 这个倍数贡献更大,一定会被先选入,所以满足上面的约束


碎梦

小诗梦到了一串密码,她将这串密码记作字符串 \(S\)

这串密码长度为 \(n\),且每一位都是 \([1,k]\) 内的正整数,她规定了一种回忆方式:

小诗事先写下一个很长的字符串 \(T\),并检验 \(T\) 每个长为 \(n\) 的子串是否等于 \(S\)。由于她忘记了所有关于 \(S\) 的信息,\(T\) 必须满足,无论 \(S\) 形态结构如何,都能在检验完所有长为 \(n\) 子串后猜出 \(S\)

她想知道,这样的 \(T\) 长度最短是多少?由于答案过大,你只需告诉她长度对 \(10^9+7\) 取模后的结果

输入

一行,两个正整数 \(n,k\)

输出

一行一个非负整数,表示答案对 \(10^9+7\) 取模后的结果

1 1
0
2 2
4
3 20
8001
20 2
1048594
8 8
16777222
1000000000 1000000000
312556836

样例解释 #1:

不用检验我们都知道答案是 1

样例解释 #2:

\(T\)1122,那么我们能检验 \(S\) 是否为 11,12,22,若 \(S\)21 我们也能根据结果推断出

\(1\leqslant n,k\leqslant 10^9\)

image


\(\max_{r-l \geq k} gcd(a_l,a_r)\)

\(n \leq 2e5,a_i \leq 1e6\)

记录每个数出现的最前位置,最后位置,倒序枚举判断 \(\leq k\) 即可

\(O(n+w)\)


给定一个长为 \(n\) 的 01 字符串 \(S\)

求对长度为 \(n\) 的字符串 \(S\) 序列有多少种操作,满足 \(A_0=S , A_n\) 为空, \(A_i\) 长 度为 \(\mathrm{n}-\mathrm{i}\) ,且 \(\forall 1 \leq i \leq n , A_i\)\(A_{i-1}\) 的子序列

定义 \(S\)\(T\) 的子序列,当且仅当可以从 \(T\) 中删去一些字符使 之变为 \(S\)

\(n \leq 400\)

D

posted @ 2023-08-03 19:44  xyzfrozen  阅读(24)  评论(0)    收藏  举报