神 の 題

开了一个新坑……
但其实本蒟蒻做题量小的可怜……
说不定就全咕咕了~

另,这里的代码一般都短的离谱
思路只不过比较清奇,并无科技含量,请放心食用

「CF1183F Topforces Strikes Back」

“至多三个数”,考虑分类讨论。

  • 如果是一个数:显然选最大值

  • 如果是两个数:其中必有一个是最大值。
    证明用反证法:
    1、若其中一个不是最大值的因数,显然另一个换成最大值最优
    2、若两个数都是最大值的因数且合法,则两数之和最大为 \(\frac{a}{2}+\frac{a}{3}\) ,还不如单选一个最大值

  • 如果是三个数:两种情况。
    1、 \(\frac{a}{2}+\frac{a}{3}+\frac{a}{5}\ \ (>a)\)
    2、从大往小选合法的即可。先选最大值,之后剩下两个数,证明同上。

code
#include<bits/stdc++.h>
using namespace std;
int ans[1145141];
int main(){
	int t;cin>>t;
	for(int k=2;k<=t+1;k++){
		int q,m,n;scanf("%d%d",&q,&m);
		ans[1]=1;
		for(int i=2;i<=m;i++){
			int lst=i-(i-1)/k-1;
			int pos=lst-ans[lst]+1;
			ans[i]=pos+(pos-1)/(k-1)+1;
		}
		while(q--){
			scanf("%d",&n);printf("%d ",ans[n]);
		}
		cout<<endl;
	}
}

「Stoi2031 枫」

@雪域亡魂,@fengwu

这类递推确实妙啊。

选完一轮之后,就知道了剩下几个点。
剩下的部分是一个子问题,考虑如何从子问题答案推到当前问题答案。

设剩下的点数为 \(l\) ,答案是 \(ans_{l}\)
那么就知道了当前问题答案的点与子问题答案的点是同一个点,只需算出编号即可。
因为每操作完一次要反向,先算出子问题里答案点的反向编号。
然后就知道了答案点之前删去了几个点。
然后……算一下就完了。

code
#include<bits/stdc++.h>
using namespace std;
int ans[1145141];
int main(){
	int t;cin>>t;
	for(int k=2;k<=t+1;k++){
		int q,m,n;scanf("%d%d",&q,&m);
		ans[1]=1;
		for(int i=2;i<=m;i++){
			int lst=i-(i-1)/k-1;
			int pos=lst-ans[lst]+1;
			ans[i]=pos+(pos-1)/(k-1)+1;
		}
		while(q--){
			scanf("%d",&n);printf("%d ",ans[n]);
		}
		cout<<endl;
	}
}

然后这类递推的经典题是约瑟夫问题,模板好像可做。

「Stoi2031 黑色毛衣」

待更。

「HNOI2012 集合选数」

待更,但是这道题完美地证明了一个定理:

一道好题是需要先分析性质才能看出做法的。

「HNOI2011 卡农」

已更。

首先转化题意,就是在 \([\ 1,2^n-1\ ]\) 选出 \(m\) 个数,满足两两不同且异或和为 \(0\) ,求方案数。题目要求无序,可以先做有序的,再除以 \(m!\)

考虑 $DP$ 或容斥。

\(f_i\) 表示选 \(i\) 个数满足以上条件的方案数。
考虑如果乱选 \(i-1\) 个不重复且不为零的数,则最后一个数随之确定。
此时答案为 \(A_{2^n-1}^{i-1}\)
因为此时前 \(i-1\) 位都满足条件了,
考虑加上对最后一位的限制,减去不合法的情况。

  • 首先,如果最后一位不得不填 \(0\) ,说明前 \(i-1\) 个数异或和为 \(0\) ,直接减去 \(f_{i-1}\)
  • 然后如果最后一位不得不填之前填过的数,说明有 \(i-2\) 个数异或和为 \(0\) ,而共有 \(i-1\) 个位置可以与最后一位相等,所以最后一位和之前某位相等的方案数为 \(f_{i-2}\times(i-1)\) 。不管最后一位填什么,只要不与剩下的 \(i-2\) 个数相同,都有这么多种情况需要减,所以还要乘 \(2^n-1-(i-2)\)
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL mod=1e8+7;
LL pw(LL bas,LL num){
	LL ans=1;
	while(num){
		if(num&1)ans=ans*bas%mod;
		bas=bas*bas%mod;num>>=1;
	}
	return ans;
}
const int N=1e6+60;
LL f[N],A[N];
int main(){
	LL n,m,jc;
	cin>>n>>m;
	LL kn=pw(2,n)-1ll;
	A[0]=1,jc=1;
	for(LL i=1;i<=m;i++){
		A[i]=A[i-1]*(kn-i+1)%mod;
		jc=jc*i%mod;
	}
	f[1]=0;f[2]=0;
	for(LL i=3;i<=m;i++){
		f[i]=(A[i-1]-f[i-1]+mod)%mod;
		f[i]=(f[i]-f[i-2]*(i-1)%mod*(kn-i+2)%mod+mod)%mod;
	}
	cout<<f[m]*pw(jc,mod-2)%mod<<endl;
}
posted @ 2021-10-20 09:43  大不美列坚  阅读(80)  评论(1)    收藏  举报