#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20000,M=2000000;
int h[N], e[M], ne[M], idx;
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int match[N],st[N];
bool find(int x)
{
for (int i = h[x]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j])
{
st[j] = true;
if (match[j] == 0 || find(match[j]))
{
match[j] = x;
return true;
}
}
}
return false;
}
int main(){
int n1,n2,m;
cin>>n1>>n2>>m;
memset(h, -1, sizeof h);
while(m--){
int a,b;
cin>>a>>b;
add(a,b);
}
int ans=0;
for(int i=1;i<=n1;i++){
memset(st, 0, sizeof st);
if(find(i))ans++;
}
cout<<ans;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 200,M=20000;
int h[N], e[M], ne[M], idx;
int match[N],st[N];
int n,m;
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool find(int x){
for(int i=h[x];~i;i=ne[i]){
int j=e[i];
if(st[j])continue;
st[j]=1;
if(match[j]==0||find(match[j])){
match[j]=x;
return 1;
}
}
return 0;
}
int main(){
cin>>m>>n;
memset(h, -1, sizeof h);
while(1){
int a,b;
cin>>a>>b;
if(a==-1)break;
add(b,a);
}
int ans=0;
for(int i=m+1;i<=n;i++){
memset(st, 0, sizeof st);
if(find(i))ans++;
}
printf("%d\n",ans);
for(int i=1;i<=m;i++)if(match[i])printf("%d %d\n",i,match[i]);
}
01背包
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
int a,b;
int dp[1001];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a>>b;
for(int j=m;j>=a;j--)dp[j]=max(dp[j],dp[j-a]+b);
}
cout<<dp[m];
}
完全背包
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
int dp[1001];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int v,w;
cin>>v>>w;
for(int j=v;j<=m;j++)dp[j]=max(dp[j],dp[j-v]+w);
}
cout<<dp[m];
}
多重背包
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
int v[1000],w[1000],s[1000];
int dp[1000][1000];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i]>>s[i];
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
for(int k=0;k<=s[i]&&k*v[i]<=j;k++){
dp[i][j]=max(dp[i][j] , dp[i-1][j-k*v[i]]+k*w[i]);
}
}
}
cout<<dp[n][m];
}
二进制优化
(就是把多重背包问题转化成01背包
(优化第三层,不用一个个去枚举
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20001;
int v[N],w[N],n,m,dp[N],cnt;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int a,b,s;
cin>>a>>b>>s;
int k=1;
while(k<=s){
cnt++;
v[cnt]=k*a;
w[cnt]=k*b;
s-=k;
k*=2;
}
if(s>0){
cnt++;
v[cnt]=s*a;
w[cnt]=s*b;
}
}
//转换成了01背包问题(正序遍历会出错,要逆序)
for(int i=1;i<=cnt;i++){
for(int j=m;j>=v[i];j--){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[m];
}
分组背包
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105;
int v[N][N],w[N][N],s[N],n,m,dp[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s[i];
for(int j=1;j<=s[i];j++)
cin>>v[i][j]>>w[i][j];
}
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--){
for(int k=1;k<=s[i];k++){
if(j>=v[i][k])dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);
}
}
}
cout<<dp[m];
}
最长上升子序列解题报告
给定一个长度为N的数列(w[N]),求数值严格单调递增的子序列的长度最长是多少。
样例
输入格式
第一行包含整数N。
第二行包含N个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
数据范围
1 ≤ N ≤ 1000,
−1e9 ≤ 数列中的数 ≤ 1e9
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4
算法1
(动态规划) O(n2)O(n2)
状态表示:f[i]表示从第一个数字开始算,以w[i]结尾的最大的上升序列。(以w[i]结尾的所有上升序列中属性为最大值的那一个)
状态计算(集合划分):j∈(0,1,2,…,i-1), 在w[i] > w[j]时,
f[i] = max(f[i], f[j] + 1)。
有一个边界,若前面没有比i小的,f[i]为1(自己为结尾)。
最后在找f[i]的最大值。
时间复杂度
O(n2)O(n2) 状态数(nn) * 转移数(nn)
C++ 代码
#include <iostream>
using namespace std;
const int N = 1010;
int n;
int w[N], f[N];
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> w[i];
int mx = 1; // 找出所计算的f[i]之中的最大值,边算边找
for (int i = 0; i < n; i++) {
f[i] = 1; // 设f[i]默认为1,找不到前面数字小于自己的时候就为1
for (int j = 0; j < i; j++) {
if (w[i] > w[j]) f[i] = max(f[i], f[j] + 1); // 前一个小于自己的数结尾的最大上升子序列加上自己,即+1
}
mx = max(mx, f[i]);
}
cout << mx << endl;
return 0;
}
算法2
(动态规划 + 二分) O(nlogn)O(nlogn)
状态表示:f[i]表示长度为i的最长上升子序列,末尾最小的数字。(长度为i的最长上升子序列所有结尾中,结尾最小min的) 即长度为i的子序列末尾最小元素是什么。
状态计算:对于每一个w[i], 如果大于f【cnt-1】(下标从0开始,cnt长度的最长上升子序列,末尾最小的数字),那就cnt+1,使得最长上升序列长度+1,当前末尾最小元素为w[i]。 若w[i]小于等于f[cnt-1],说明不会更新当前的长度,但之前末尾的最小元素要发生变化,找到第一个 大于或等于 (这里不能是大于) w[i],更新以那时候末尾的最小元素。
f[i]一定以一个单调递增的数组,所以可以用二分法来找第一个大于或等于w[i]的数字。
时间复杂度
O(nlogn)O(nlogn) 状态数(nn) * 转移数(lognlogn)
C++ 代码
#include <iostream>
using namespace std;
const int N = 1010;
int n, cnt;
int w[N], f[N];
int main() {
cin >> n;
for (int i = 0 ; i < n; i++) cin >> w[i];
f[cnt++] = w[0];
for (int i = 1; i < n; i++) {
if (w[i] > f[cnt-1]) f[cnt++] = w[i];
else {
int l = 0, r = cnt - 1;
while (l < r) {
int mid = (l + r) >> 1;
if (f[mid] >= w[i]) r = mid;
else l = mid + 1;
}
f[r] = w[i];
}
}
cout << cnt << endl;
return 0;
}
作者:VictorWu
链接:https://www.acwing.com/solution/content/4807/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
还有个用堆栈模拟和lower_bound()更简单更牛逼
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main(void) {
int n; cin >> n;
vector<int>arr(n);
for (int i = 0; i < n; ++i)cin >> arr[i];
vector<int>stk;//模拟堆栈
stk.push_back(arr[0]);
for (int i = 1; i < n; ++i) {
if (arr[i] > stk.back())//如果该元素大于栈顶元素,将该元素入栈
stk.push_back(arr[i]);
else//替换掉第一个大于或者等于这个数字的那个数
*lower_bound(stk.begin(), stk.end(), arr[i]) = arr[i];
}
cout << stk.size() << endl;
return 0;
}
/*
例 n: 7
arr : 3 1 2 1 8 5 6
stk : 3
1 比 3 小
stk : 1
2 比 1 大
stk : 1 2
1 比 2 小
stk : 1 2
8 比 2 大
stk : 1 2 8
5 比 8 小
stk : 1 2 5
6 比 5 大
stk : 1 2 5 6
stk 的长度就是最长递增子序列的长度
*/
作者:233
链接:https://www.acwing.com/solution/content/3783/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最长公共子序列
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
char a[N],b[N];
int dp[N][N];
int n,m;
signed main(){
cin>>n>>m>>a+1>>b+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]==b[j])dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
cout<<dp[n][m];
}
给定两个字符串 A 和 B,现在要将 A 经过若干操作变为 B,可进行的操作有:
删除–将字符串 A 中的某个字符删除。
插入–在字符串 A 的某个位置插入某个字符。
替换–将字符串 A 中的某个字符替换为另一个字符。
现在请你求出,将 A 变为 B 至少需要进行多少次操作。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
char a[N],b[N];
int dp[N][N];
//dp[i][j]表示a的前i个变成b的前j个所需的最少操作次数
int n,m;
int minn(int a,int b,int c){
return min(a,min(b,c));
}
signed main(){
cin>>n>>a+1>>m>>b+1;
//入口
for(int i=0;i<=n;i++)dp[i][0]=i;//全删除所需的操作次数
for(int j=0;j<=m;j++)dp[0][j]=j;//全插入所需的操作次数
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]==b[j])dp[i][j]=dp[i-1][j-1];
else dp[i][j]=minn(dp[i-1][j-1] , dp[i-1][j] , dp[i][j-1])+1;
//替换 插入 删除
}
}
//出口
cout<<dp[n][m];
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
char a[N],b[N];
int dp[N][N];
char c[N][11];
int k;
//dp[i][j]表示a的前i个变成b的前j个所需的最少操作次数
int n,m;
int minn(int a,int b,int c){
return min(a,min(b,c));
}
int cal(char *a,char*b){
int n=strlen(a+1);
int m=strlen(b+1);
//cout<<n<<m<<endl;
for(int i=0;i<=n;i++)dp[i][0]=i;//全删除所需的操作次数
for(int j=0;j<=m;j++)dp[0][j]=j;//全插入所需的操作次数
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]==b[j])dp[i][j]=dp[i-1][j-1];
else dp[i][j]=minn(dp[i-1][j-1] , dp[i-1][j] , dp[i][j-1])+1;
//替换 插入 删除
}
}
return dp[n][m];
}
int solve(){
cin>>a+1>>k;
int ans=0;
for(int i=1;i<=n;i++)
if(cal(a,c[i])<=k)ans++;
cout<<ans<<endl;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>c[i]+1;
while(m--)solve();
}
石子合并
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 330;
int a[N],s[N],f[N][N],n;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
memset(f,0x3f,sizeof f);
for(int i=0;i<=n;i++)f[i][i]=0;
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int k=l;k<r;k++){
f[l][r]=min(f[l][r] , f[l][k]+f[k+1][r]+s[r]-s[l-1]);
}
}
}
cout<<f[1][n];
}
整数划分
完全背包解法
状态表示:
f[i][j]表示只从1~i中选,且总和等于j的方案数
状态转移方程:
f[i][j] = f[i - 1][j] + f[i][j - i];
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int n;
int f[N];
int main()
{
cin >> n;
f[0] = 1;
for (int i = 1; i <= n; i ++ )
for (int j = i; j <= n; j ++ )
f[j] = (f[j] + f[j - i]) % mod;
cout << f[n] << endl;
return 0;
}
其他算法
状态表示:
f[i][j]表示总和为i,总个数为j的方案数
状态转移方程:
f[i][j] = f[i - 1][j - 1] + f[i - j][j];
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int n;
int f[N][N];
int main()
{
cin >> n;
f[1][1] = 1;
for (int i = 2; i <= n; i ++ )
for (int j = 1; j <= i; j ++ )
f[i][j] = (f[i - 1][j - 1] + f[i - j][j]) % mod;
int res = 0;
for (int i = 1; i <= n; i ++ ) res = (res + f[n][i]) % mod;
cout << res << endl;
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/62496/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
给定两个整数 a 和 b,求 a 和 b 之间的所有数字中 0∼9 的出现次数。
例如,a=1024,b=1032,则 a 和 b 之间共有 9 个数如下:
1024 1025 1026 1027 1028 1029 1030 1031 1032
其中 0 出现 10 次,1 出现 10 次,2 出现 7 次,3 出现 3 次等等…
输入格式
输入包含多组测试数据。
每组测试数据占一行,包含两个整数 a 和 b。
当读入一行为 0 0 时,表示输入终止,且该行不作处理。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 10;
/*
001~abc-1, 999
abc
1. num[i] < x, 0
2. num[i] == x, 0~efg
3. num[i] > x, 0~999
*/
int get(vector<int> num, int l, int r)
{
int res = 0;
for (int i = l; i >= r; i -- ) res = res * 10 + num[i];
return res;
}
int power10(int x)
{
int res = 1;
while (x -- ) res *= 10;
return res;
}
int count(int n, int x)
{
if (!n) return 0;
vector<int> num;
while (n)
{
num.push_back(n % 10);
n /= 10;
}
n = num.size();
int res = 0;
for (int i = n - 1 - !x; i >= 0; i -- )
{
if (i < n - 1)
{
res += get(num, n - 1, i + 1) * power10(i);
if (!x) res -= power10(i);
}
if (num[i] == x) res += get(num, i - 1, 0) + 1;
else if (num[i] > x) res += power10(i);
}
return res;
}
int main()
{
int a, b;
while (cin >> a >> b , a)
{
if (a > b) swap(a, b);
for (int i = 0; i <= 9; i ++ )
cout << count(b, i) - count(a - 1, i) << ' ';
cout << endl;
}
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/64211/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
蒙德里安的梦想
求把 N×M 的棋盘分割成若干个 1×2 的的长方形,有多少种方案。
例如当 N=2,M=4 时,共有 5 种方案。当 N=2,M=3 时,共有 3 种方案。
如下图所示:
2411_1.jpg
输入格式
输入包含多组测试用例。
每组测试用例占一行,包含两个整数 N 和 M。
当输入用例 N=0,M=0 时,表示输入终止,且该用例无需处理。
#include<bits/stdc++.h>
using namespace std;
const int N = 12,M=1<<N;
long long dp[N][M];
bool st[M];
int n,m;
signed main(){
while(cin>>n>>m,n||m){
for(int i=0;i<1<<n;i++){
st[i]=1;
int cnt=0;
for(int j=0;j<n;j++){
if(i>>j&1){
if(cnt&1){st[i]=0;break;}
cnt=0;
}
else cnt++;
}
if(cnt&1)st[i]=0;
}
memset(dp,0,sizeof dp);
dp[0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<1<<n;j++){
for(int k=0;k<1<<n;k++){
if((j&k)==0&&st[j|k])
dp[i][j]+=dp[i-1][k];
}
}
}
cout<<dp[m][0]<<endl;
}
}
给定一张 n 个点的带权无向图,点从 0∼n−1 标号,求起点 0 到终点 n−1 的最短 Hamilton 路径。
Hamilton 路径的定义是从 0 到 n−1 不重不漏地经过每个点恰好一次。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20,M=1<<20;
int w[N][N];
int dp[M][N];
int n;
signed main(){
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)cin>>w[i][j];
memset(dp,0x3f,sizeof dp);
dp[1][0]=0;
for(int i=0;i<1<<n;i++){
for(int j=0;j<n;j++){
if(i>>j&1)
for(int k=0;k<n;k++){
if(i>>k&1)
dp[i][j]=min(dp[i][j] , dp[i-(1<<j)][k]+w[k][j]);
}
}
}
cout<<dp[(1<<n)-1][n-1];
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 7000,M=N;
int a[N],n;
int dp[N][2];
int h[N], e[M], ne[M], idx;
void add(int a, int b) // 添加一条边a->b,边权为c
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int rudu[N];
void dfs(int u){
dp[u][1]=a[u];
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
dfs(j);
dp[u][1]+=dp[j][0];
dp[u][0]+=max(dp[j][0],dp[j][1]);
}
}
signed main(){
memset(h, -1, sizeof h);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<n;i++){
int L,K;
cin>>L>>K;
add(K,L);
rudu[L]++;
}
int root=-1;
for(int i=1;i<=n;i++){
if(rudu[i]==0){
root=i;
break;
}
}
dfs(root);
cout<<max(dp[root][1],dp[root][0]);
}
滑雪(记忆化搜索
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[301][301],dp[301][301];
int n,m;
int xx[]={1,0,-1,0};
int yy[]={0,1,0,-1};
int dfs(int x,int y){
int &t=dp[x][y];
if(t)return t;
for(int i=0;i<4;i++){
int dx=x+xx[i];
int dy=y+yy[i];
if(a[dx][dy]<=a[x][y])continue;
t=max(t,dfs(dx,dy)+1);
}
return t;
}
int main(){
cin>>n>>m;
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)ans=max(ans,dfs(i,j));
}
cout<<ans+1;
}
试除法
时间复杂度O(T*根号n)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,t;
bool is_prime(int x){
if(x<2)return 0;
for(int i=2;i<=n/i;i++){
if(n%i==0)return 0;
}
return 1;
}
int main(){
cin>>t;
while(t--){
cin>>n;
if(is_prime(n))puts("Yes");
else puts("No");
}
}
分解质因数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int T,n;
void divid(int x){
for(int i=2;i<=x/i;i++){
if(x%i==0){
int s=0;
while(x%i==0)x/=i,s++;
cout<<i<<" "<<s<<endl;
}
}
if(x>1)cout<<x<<" "<<1<<endl;
}
signed main(){
cin>>T;
while(T--){
cin>>n;
divid(n);
cout<<endl;
}
}
给定一个正整数 n,请你求出 1∼n 中质数的个数。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6;
int n;
int st[N],primes[N],cnt;
void get_primes(int n) // 线性筛质数
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int main(){
cin>>n;
get_primes(n);
cout<<cnt;
}
试除法求约数(我用优先队列来做省去了用vector做要sort的麻烦
#include<bits/stdc++.h>
using namespace std;
int x;
priority_queue<int,vector<int>,greater<int>> get_divisors(int x){
priority_queue<int,vector<int>,greater<int>>q;
int i=1;
for(;i<x/i;i++){
if(x%i==0)q.push(i),q.push(x/i);
}
if(i*i==x)q.push(i);
return q;
}
int main(){
int T;
cin>>T;
while(T--){
cin>>x;
auto q=get_divisors(x);
while(!q.empty()){
cout<<q.top()<<" ";
q.pop();
}
puts("");
}
}
给定 n 个正整数 ai,请你求出每个数的欧拉函数。
欧拉函数的定义
1∼N 中与 N 互质的数的个数被称为欧拉函数,记为 ϕ(N)。
若在算数基本定理中,N=pa11pa22…pamm,则:
ϕ(N) = N×p1−1p1×p2−1p2×…×pm−1pm
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个正整数 ai。
输出格式
输出共 n 行,每行输出一个正整数 ai 的欧拉函数。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int T,n;
int phi(int x){
int ans=x;
for(int i=2;i<=x/i;i++){
if(x%i==0){
ans=ans/i*(i-1);
while(x%i==0)x/=i;
}
}
if(x>1)ans=ans/x*(x-1);
return ans;
}
int main(){
cin>>T;
while(T--){
cin>>n;
cout<<phi(n)<<endl;
}
}
#include <unordered_map>
#include <iostream>
#include <cstring>
#include <algorithm>
#define hash h
using namespace std;
const int mod=1e9+7;
int x;
int T;
unordered_map<int,int>hash;
int main(){
cin>>T;
while(T--){
cin>>x;
for(int i=2;i<=x/i;i++){
while(x%i==0){
x/=i;
hash[i]++;
}
}
if(x>1)hash[x]++;
}
long long ans=1;
for(auto t:hash)ans*=(t.second+1);
cout<<ans;
}
约数之和
算法分析
基本思想:
如果 N=p1c1∗p2c2∗…∗pkckN=p1c1∗p2c2∗…∗pkck
约数个数:(c1+1)∗(c2+1)∗…∗(ck+1)(c1+1)∗(c2+1)∗…∗(ck+1)
约数之和: (p10+p11+…+p1c1)∗…∗(pk0+pk1+…+pkck)(p10+p11+…+p1c1)∗…∗(pk0+pk1+…+pkck)
while (b – ) t = (t * a + 1) % mod;
t=t∗p+1t=t∗p+1
t=1t=1
t=p+1t=p+1
t=p2+p+1t=p2+p+1
……
t=pb+pb−1+…+1
(当然你也可以用快速幂提速
#include<bits/stdc++.h>
#define hash h
using namespace std;
int T,n;
const int mod=1e9+7;
long long ans=1;
signed main(){
cin>>T;
unordered_map<int,int>hash;
while(T--){
cin>>n;
for(int i=2;i<=n/i;i++){
while(n%i==0){
n/=i;
hash[i]++;
}
}
if(n>1)hash[n]++;
}
for(auto t:hash){
int a=t.first;
int b=t.second;
long long res=1;
while(b--)res=(res*a+1)%mod;
ans=(ans*res)%mod;
}
cout<<ans;
}
给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和。
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1000010;
int primes[N], cnt;
int phi[N];
bool st[N];
void get_eulers(int n)
{
phi[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!st[i])
{
primes[cnt++] = i;
phi[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; j++)
{
st[primes[j] * i] = true;
if (i % primes[j] == 0)
{
phi[primes[j] * i] = phi[i] * primes[j];
break;
}
phi[primes[j] * i] = phi[i] * (primes[j] - 1);
}
}
}
int main()
{
int n;
cin >> n;
get_eulers(n);
LL res = 0;
for (int i = 1; i <= n; i++) res += phi[i];
printf("%lld\n", res);
return 0;
}
作者:番茄酱
链接:https://www.acwing.com/solution/content/3952/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
int qmi(int a,int b,int q){
int ans=1;
while(b){
if(b&1)ans=(ans*a)%q;
a=(a*a)%q;
b/=2;
}
return ans;
}
signed main(){
int t;
cin>>t;
while(t--){
int a,b,p;
cin>>a>>b>>p;
cout<<qmi(a,b,p)<<endl;
}
}
当n为质数时,可以用快速幂求逆元:
a / b ≡ a * x (mod n)
两边同乘b可得 a ≡ a * b * x (mod n)
即 1 ≡ b * x (mod n)
同 b * x ≡ 1 (mod n)
由费马小定理可知,当n为质数时
b ^ (n - 1) ≡ 1 (mod n)
拆一个b出来可得 b * b ^ (n - 2) ≡ 1 (mod n)
故当n为质数时,b的乘法逆元 x = b ^ (n - 2)
当n不是质数时,可以用扩展欧几里得算法求逆元:
a有逆元的充要条件是a与p互质,所以gcd(a, p) = 1
假设a的逆元为x,那么有a * x ≡ 1 (mod p)
等价:ax + py = 1
exgcd(a, p, x, y)
快速幂求逆元
#include <iostream>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1;
while(b){
if(b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n; cin >> n;
while(n --){
int a, p;
cin >> a >> p;
if(a % p == 0) puts("impossible");
else cout << qmi(a, p - 2, p) << endl;
}
return 0;
}
扩展欧几里得算法求逆元
#include <iostream>
using namespace std;
typedef long long LL;
int n;
int exgcd(int a, int b, int &x, int &y)
{
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
cin >> n;
while (n --)
{
int a, p, x, y;
// if (a < p) swap(a, p);
cin >> a >> p;
int d = exgcd(a, p, x, y);
if (d == 1) cout << ((LL)x + p) % p << endl;//保证x是正数
else puts("impossible");
}
return 0;
}
作者:Hz
链接:https://www.acwing.com/solution/content/3054/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
给定 n 组数据 ai,bi,mi,对于每组数求出一个 xi,使其满足 ai×xi≡bi(modmi),如果无解则输出 impossible。
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
int a, b, m;
scanf("%d%d%d", &a, &b, &m);
int x, y;
int d = exgcd(a, m, x, y);
if (b % d) puts("impossible");//如果b与公约数gcd不互质就无解
else printf("%d\n", (LL)b / d * x % m);
}
return 0;-
}
扩展中国剩余定理
给定 2n 个整数 a1,a2,…,an 和 m1,m2,…,mn,求一个最小的非负整数 x,满足 ∀i∈[1,n],x≡mi(mod ai)。
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long LL;
int n;
LL exgcd(LL a, LL b, LL &x, LL &y){
if(b == 0){
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
LL inline mod(LL a, LL b){
return ((a % b) + b) % b;
}
int main(){
scanf("%d", &n);
LL a1, m1;
scanf("%lld%lld", &a1, &m1);
for(int i = 1; i < n; i++){
LL a2, m2, k1, k2;
scanf("%lld%lld", &a2, &m2);
LL d = exgcd(a1, -a2, k1, k2);
if((m2 - m1) % d){ puts("-1"); return 0; }
k1 = mod(k1 * (m2 - m1) / d, abs(a2 / d));
m1 = k1 * a1 + m1;
a1 = abs(a1 / d * a2);
}
printf("%lld\n", m1);
return 0;
}
作者:墨染空
链接:https://www.acwing.com/solution/content/3539/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
高斯消元解线性方程组
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 110;
const double eps = 1e-6;
int n;
double a[N][N];
int gauss()
{
int c, r;
for (c = 0, r = 0; c < n; c ++ )
{
int t = r;
for (int i = r; i < n; i ++ )
if (fabs(a[i][c]) > fabs(a[t][c]))
t = i;
if (fabs(a[t][c]) < eps) continue;
for (int i = c; i < n + 1; i ++ ) swap(a[t][i], a[r][i]);
for (int i = n; i >= c; i -- ) a[r][i] /= a[r][c];
for (int i = r + 1; i < n; i ++ )
if (fabs(a[i][c]) > eps)
for (int j = n; j >= c; j -- )
a[i][j] -= a[r][j] * a[i][c];
r ++ ;
}
if (r < n)
{
for (int i = r; i < n; i ++ )
if (fabs(a[i][n]) > eps)
return 2;
return 1;
}
for (int i = n - 1; i >= 0; i -- )
for (int j = i + 1; j < n; j ++ )
a[i][n] -= a[j][n] * a[i][j];
return 0;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n + 1; j ++ )
cin >> a[i][j];
int t = gauss();
if (t == 0)
{
for (int i = 0; i < n; i ++ ) printf("%.2lf\n", a[i][n]);
}
else if (t == 1) puts("Infinite group solutions");
else puts("No solution");
return 0;
}
高斯消元解异或线性方程组
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
int a[N][N];
int gauss()
{
int c, r;
for (c = 0, r = 0; c < n; c ++ )
{
int t = r;
for (int i = r; i < n; i ++ )
if (a[i][c])
t = i;
if (!a[t][c]) continue;
for (int i = c; i <= n; i ++ ) swap(a[r][i], a[t][i]);
for (int i = r + 1; i < n; i ++ )
if (a[i][c])
for (int j = n; j >= c; j -- )
a[i][j] ^= a[r][j];
r ++ ;
}
if (r < n)
{
for (int i = r; i < n; i ++ )
if (a[i][n])
return 2;
return 1;
}
for (int i = n - 1; i >= 0; i -- )
for (int j = i + 1; j < n; j ++ )
a[i][n] ^= a[i][j] * a[j][n];
return 0;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n + 1; j ++ )
cin >> a[i][j];
int t = gauss();
if (t == 0)
{
for (int i = 0; i < n; i ++ ) cout << a[i][n] << endl;
}
else if (t == 1) puts("Multiple sets of solutions");
else puts("No solution");
return 0;
}
求组合数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod=1e9+7;
int c[2001][2001];
int a,b;
void init(){
for(int i=0;i<=2000;i++){
for(int j=0;j<=i;j++){
if(!j)c[i][j]=1;
else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
}
int main(){
init();
int T;
cin>>T;
while(T--){
cin>>a>>b;
cout<<c[a][b]<<endl;
}
}
组合数Ⅱ
当给出的a,b数据范围扩大时
1≤n≤10000,
1≤b≤a≤105
O(n) 时间复杂度的极速解法
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<int> getRangeModularMultInv(int n, int p) {
vector<int> inv(n + 1);
inv[1] = 1;
for (int i = 2; i <= n; ++i)
inv[i] = (LL)(p - p / i) * inv[p % i] % p;
return inv;
}
const int N = 100010, MOD = 1e9 + 7;
int fac[N], invFac[N];
void calcFac(int n, int m) {
auto invI = getRangeModularMultInv(n, m);
fac[0] = 1; invFac[0] = 1;
for (int i = 1; i < N; i++) {
fac[i] = (LL)fac[i - 1] * i % m;
invFac[i] = (LL)invFac[i - 1] * invI[i] % m;
}
}
int getC(int a, int b, int m) {
return (LL)fac[a] * invFac[a - b] % m * invFac[b] % m;
}
int main() {
int q; cin >> q;
calcFac(N, MOD);
while (q--) {
int a, b; cin >> a >> b;
cout << getC(a, b, MOD) << endl;
}
return 0;
}
作者:zan
链接:https://www.acwing.com/solution/content/22453/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
快速幂求组合数 O(a∗log(mod))O(a∗log(mod))
#include<iostream>
using namespace std;
const int mod=1e9+7,N=1e5+10;
typedef long long LL;
long long fac[N],infac[N];
int quick_pow(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main()
{
int n;
fac[0]=infac[0]=1;
for(int i=1;i<=1e5;i++)
{
fac[i]=fac[i-1]*i%mod;
infac[i]=(LL)infac[i - 1] * quick_pow(i,mod-2,mod)%mod;
}
cin>>n;
while(n--)
{
int a,b;
cin>>a>>b;
cout<<(LL)fac[a] * infac[b] % mod * infac[a - b] % mod<<endl;
//Cab=a的阶乘除以(b的阶乘*(a-b)的阶乘)
//等价于a!*b逆元*(a-b)逆元
}
}
作者:码
链接:https://www.acwing.com/solution/content/22076/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
AcWing 887. 求组合数 III(lucas定理)
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int qmi(int a,int k,int p)
{
int res = 1;
while(k)
{
if(k&1)res = (LL)res*a%p;
a = (LL)a*a%p;
k>>=1;
}
return res;
}
int C(int a,int b,int p)//自变量类型int
{
if(b>a)return 0;//漏了边界条件
int res = 1;
// a!/(b!(a-b)!) = (a-b+1)*...*a / b! 分子有b项
for(int i=1,j=a;i<=b;i++,j--)//i<=b而不是<
{
res = (LL)res*j%p;
res = (LL)res*qmi(i,p-2,p)%p;
}
return res;
}
//对公式敲
int lucas(LL a,LL b,int p)
{
if(a<p && b<p)return C(a,b,p);//lucas递归终点是C_{bk}^{ak}
return (LL)C(a%p,b%p,p)*lucas(a/p,b/p,p)%p;//a%p后肯定是<p的,所以可以用C(),但a/p后不一定<p 所以用lucas继续递归
}
int main()
{
int n;
cin >> n;
while(n--)
{
LL a,b;
int p;
cin >> a >> b >> p;
cout << lucas(a,b,p) << endl;
}
return 0;
}
作者:仅存老实人
链接:https://www.acwing.com/solution/content/26553/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题解有个人对C(a,b,p)函数做了简单的优化,板子比yxc快了10倍
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9+7;
int fact[N], infact[N];
int qmi(int a, int k, int p)
{
int res = 1;
while(k)
{
if(k & 1)
res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int C(int a, int b, int p)
{
if(a < b)return 0;
int down = 1, up = 1;
for(int i=a, j=1; j<=b; i--, j++)
{
up = (LL)up * i % p;
down = (LL)down * j % p;
}
return (LL)up * qmi(down, p-2, p) % p;
}
int lucas(LL a, LL b, int p)
{
if(a<p && b<p)return C(a, b, p);
else return (LL)C(a%p, b%p, p)*lucas(a/p, b/p, p)%p;
}
int main()
{
int n;
scanf("%d", &n);
while(n--)
{
LL a, b, p;
cin>>a>>b;
cin>>p;
cout<<lucas(a, b, p)<<endl;
}
return 0;
}
求组合数Ⅳ
不mod p要用高精度计算出结果
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 5010;
int primes[N], cnt;
int sum[N];
bool st[N];
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int get(int n, int p)
{
int res = 0;
while (n)
{
res += n / p;
n /= p;
}
return res;
}
vector<int> mul(vector<int> a, int b)
{
vector<int> c;
int t = 0;
for (int i = 0; i < a.size(); i ++ )
{
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while (t)
{
c.push_back(t % 10);
t /= 10;
}
return c;
}
int main()
{
int a, b;
cin >> a >> b;
get_primes(a);
for (int i = 0; i < cnt; i ++ )
{
int p = primes[i];
sum[i] = get(a, p) - get(a - b, p) - get(b, p);
}
vector<int> res;
res.push_back(1);
for (int i = 0; i < cnt; i ++ )
for (int j = 0; j < sum[i]; j ++ )
res = mul(res, primes[i]);
for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);
puts("");
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/53401/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
满足条件的01序列
把问题放到坐标系中去解决
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int qmi(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int C(int a, int b, int p)
{
if (b > a) return 0;
int res = 1;
for (int i = 1, j = a; i <= b; i ++, j -- )
{
res = (LL)res * j % p;
res = (LL)res * qmi(i, p - 2, p) % p;
}
return res;
}
int lucas(LL a, LL b, int p)
{
if (a < p && b < p) return C(a, b, p);
return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
LL a, b;
int p;
cin >> a >> b >> p;
cout << lucas(a, b, p) << endl;
}
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/53399/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
火车进出栈问题(卡特拉数经典应用
一列火车n节车厢,依次编号为1,2,3,…,n1,2,3,…,n。
每节车厢有两种运动方式,进栈与出栈,问n节车厢出栈的可能排列方式有多少种。
这道题也是求卡特兰数,不同的是这道题需要高精度和卡时
#include <stdio.h>
const int N = 11311;
const int base = 1e9; // 压 9 位
const int basebit = 9;
int n;
int sum[N]; // 存筛出的质数及其次数的结果
long long res[4500]; // 存答案,res[0] 存 res 的位数
int primes[N], cnt; // 存筛的质数
bool st[120001]; // 筛质数用的 bool 数组
inline void init() // 线性筛质数 + 求分解质因数的结果
{
for (register int i = 2; i <= n << 1; i ++ )
{
if (!st[i]) // 如果 st[i] == false,说明该数 i 是质数
{
primes[ ++ cnt] = i; // 先把它存进 primes
for (register int j = (n << 1) / i; j; j /= i) // 加上 (2n)! 含有 i 的数量。
sum[cnt] += j;
for (register int j = n / i; j; j /= i) // 减去 (n!)^2 含有 i 的数量。
sum[cnt] -= j << 1; // (n!)^2 含有 i 的数量即两倍的 n! 所含有的 i 的数量,所以只用处理 n!,然后减去其所含数量两倍即可
for (register int j = n + 1; j % i == 0; j /= i) // 减去 n + 1 含有 i 的数量
sum[cnt] -- ;
}
for (register int j = 1; primes[j] <= (n << 1) / i; j ++ ) // 线性筛的板子
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break ;
}
}
}
inline int qmi(int a, int b) // 快速幂(其实并没有卵用。。大部分调用都会进到那个 if (b == 1) 里面去。。所以加了个特判,效率会更高一点)
{
if (b == 1) return a;
int res = 1;
for (; b; a *= a, b >>= 1)
if (b & 1) res *= a;
return res;
}
inline void multi(int b) // 将 res 乘 b
{
register long long t = 0;
for (register int i = 1; i <= res[0]; i ++ )
{
res[i] = res[i] * b + t;
t = res[i] / base;
res[i] %= base;
}
while (t) res[ ++ res[0]] = t % base, t /= base;
}
void print(int x, int i = basebit) // 快速输出 9 位
{
if (!i) return ;
print(x / 10, i - 1);
putchar(x % 10 ^ 48);
}
int main()
{
scanf("%d",&n);
init(); // 初始化 primes + 初始化 sum
res[0] = res[1] = 1; // 出初始化 res,res 的长度制为 1,res 的值制成 1
for (register int i = 1; i <= cnt; i ++ ) // 枚举一遍分解出来的所有的质数
if (sum[i]) multi(qmi(primes[i], sum[i]));
printf("%lld", res[res[0]]); // 第一位不用输出 9 位
if (res[0] > 1) // 如果 res 的位数大于一的话,那么输出后面的
for (register int i = res[0] - 1; i; i -- )
print(res[i]);
return 0;
}
作者:垫底抽风
链接:https://www.acwing.com/solution/content/15603/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
import math
n = int(input())
A = math.factorial(2 * n)
B = math.factorial(n)
print(A // B // B // (n + 1))
给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm。
请你求出 1∼n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
int primes[16];
int ans=0;
signed main(){
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++)cin>>primes[i];
//i从0开始是错的,从1开始才是对的
//这也是为了保证容斥原理最少计算一个图
for(int i=1;i<1<<m;i++){
int t=1,cnt=0;
for(int j=0;j<m;j++){
if(i>>j&1){
if(primes[j]*t>n){
t=-1;
break;
}
t*=primes[j];
cnt++;
}
}
if(t==-1)continue;
if(cnt&1)ans+=n/t;
else ans-=n/t;
}
cout<<ans;
}
中国剩余定理
#include<bits/stdc++.h>
using namespace std;
#define int long long
int mod(int a,int b){
return (a%b+b)%b;
}
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
signed main(){
int n;
cin>>n;
int a1,m1,k1;
cin>>a1>>m1;
for(int i=1;i<n;i++){
int a2,m2,k2;
cin>>a2>>m2;
int d=exgcd(a1,-a2,k1,k2);
if((m2-m1)%d){puts("-1");return 0;}
k1=mod(k1*(m2-m1)/d , abs(a2/d));
m1=m1+k1*a1;
a1=abs(a1/d*a2);
}
cout<<m1;
}
若一个游戏满足:
由两名玩家交替行动
在游戏进行的任意时刻,可以执行的合法行动与轮到哪位玩家无关
不能行动的玩家判负
则称该游戏为一个公平组合游戏。
尼姆游戏(NIM)属于公平组合游戏,但常见的棋类游戏,比如围棋就不是公平组合游戏,因为围棋交战双方分别只能落黑子和白子,胜负判定也比较负责,不满足条件2和3。
题目描述
给定nn堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
例如:有两堆石子,第一堆有2个,第二堆有3个,先手必胜。
操作步骤:
- 先手从第二堆拿走1个,此时第一堆和第二堆数目相同
- 无论后手怎么拿,先手都在另外一堆石子中取走相同数量的石子即可。
必胜状态和必败状态
在解决这个问题之前,先来了解两个名词:
必胜状态,先手进行某一个操作,留给后手是一个必败状态时,对于先手来说是一个必胜状态。即先手可以走到某一个必败状态。
必败状态,先手无论如何操作,留给后手都是一个必胜状态时,对于先手来说是一个必败状态。即先手走不到任何一个必败状态。
结论
假设nn堆石子,石子数目分别是a1,a2,…,ana1,a2,…,an,如果a1⊕a2⊕…⊕an≠0a1⊕a2⊕…⊕an≠0,先手必胜;否则先手必败。
- 如果先手面对的局面是a1⊕a2⊕…⊕an≠0a1⊕a2⊕…⊕an≠0,那么先手总可以通过拿走某一堆若干个石子,将局面变成a1⊕a2⊕…⊕an=0a1⊕a2⊕…⊕an=0。如此重复,最后一定是后手面临最终没有石子可拿的状态。先手必胜。
- 如果先手面对的局面是a1⊕a2⊕…⊕an=0a1⊕a2⊕…⊕an=0,那么无论先手怎么拿,都会将局面变成a1⊕a2⊕…⊕an≠0a1⊕a2⊕…⊕an≠0,那么后手总可以通过拿走某一堆若干个石子,将局面变成a1⊕a2⊕…⊕an=0a1⊕a2⊕…⊕an=0。如此重复,最后一定是先手面临最终没有石子可拿的状态。先手必败。
#include<bits/stdc++.h>
using namespace std;
int n;
signed main(){
cin>>n;
int ans=0;
while(n--){
int x;
cin>>x;
ans^=x;
}
if(ans==0)puts("No");
else puts("Yes");
}
现在,有一个 n 级台阶的楼梯,每级台阶上都有若干个石子,其中第 i 级台阶上有 ai 个石子(i≥1)。
两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。
已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
(核心就是:先手总是把奇数台阶异或为0的状态留给对面,即总是将必败态交给对面)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
signed main(){
cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(i&1)ans^=x;
}
if(ans)puts("Yes");
else puts("No");
}
#include<bits/stdc++.h>
using namespace std;
const int N = 110,M=10010;
int n,m;
int s[N],SG[M];
int sg(int x){
if(SG[x]!=-1)return SG[x];
unordered_set<int>S;
for(int i=0;i<m;i++){
if(x>=s[i])S.insert(sg(x-s[i]));
}
for(int i=0;;i++){
if(!S.count(i))return SG[x]=i;
}
}
signed main(){
memset(SG,-1,sizeof SG);
cin>>m;
for(int i=0;i<m;i++)cin>>s[i];
cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
int x;
cin>>x;
ans^=sg(x);
}
if(ans)puts("Yes");
else puts("No");
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;
const int N = 110;
int f[N];
int n;
int sg(int x){
if(f[x]!=-1)return f[x];
unordered_set<int>S;
for(int i=0;i<x;i++){
for(int j=0;j<=i;j++){
S.insert(sg(i)^sg(j));
}
}
//mex操作:找到集合里不存在的最小自然数
for(int i=0;;i++){
if(!S.count(i))return f[x]=i;
}
}
signed main(){
cin>>n;
memset(f,-1,sizeof f);
int ans=0;
while(n--){
int x;
cin>>x;
ans^=sg(x);
}
if(ans)puts("Yes");
else puts("No");
}
区间分组
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
struct node{
int l,r;
bool operator<(const node&t){
return l<t.l;
}
}range[N];
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++)cin>>range[i].l>>range[i].r;
priority_queue<int,vector<int>,greater<int>>heap;
sort(range,range+n);
for(int i=0;i<n;i++){
if(heap.empty()||range[i].l<=heap.top())heap.push(range[i].r);
else{
heap.pop();
heap.push(range[i].r);
}
}
cout<<heap.size();
}