C - Ubiquity(容斥原理/DP)
题意:
求满足以下条件的长为 n 的不同序列的个数:
1.0 ≤ ≤9
2.序列中至少有一个 =0
3.序列中至少有一个 =9
答案对 +7 取模。
数据范围:
1 ≤ N ≤
思路1(容斥原理):
有0有9的 = 总的 - 无0的 - 无9的 + 无0无9的
Code1:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 1e6+10, M = 3010, INF = 0x3f3f3f3f, mod = 1e9+7;
typedef pair<int, int>PII;
int n;
int qmi(int a,int k,int p)
{
int res = 1;
while(k)
{
if(k & 1)res = res * a % p;
k >>= 1;
a = a*a%p;
}
return res;
}
void solve()
{
cin >> n;
if(n == 1)cout << 0 << endl;
else cout << ((qmi(10,n,mod) - 2*qmi(9,n,mod) + qmi(8,n,mod))% mod+mod)% mod << endl;
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
思路2(DP):
状态表示:f[i,j,k]表示总共 i 个数的序列。j = 1 表示出现0,j = 0 表示没有出现 0;k = 1 表示出现9,k = 0 表示没有出现9。最终求的答案是 f[n,1,1]。
集合划分:前 i 个数的有0有9的序列可以由前 i - 1 个数的序列状态转移得到——
如果前 i - 1 个数的序列有0有9,则第 i 个数可取0~9;
如果前 i - 1 个数的序列有0无9,则第 i 个数一定取9;
如果前 i - 1 个数的序列有0无9,则第 i 个数一定取9;
如果前 i - 1 个数无0无9,则无法由该状态转移得到。
所以 f[i][1][1] = f[i-1][1][0] + f[i-1][0][1] + f[i-1][1][1]*10
可以看出,我们需要算出 f[i-1,1,0] 和 f[i-1,0,1] 如何转移得到的,同理:
对于前 i 个数有0无9的 f[i,1,0]——
如果前 i - 1 个数的序列无0无9,则第i个数一定是0;
如果前 i - 1 个数的序列有0无9,则第i个数可取0~8;
如果前 i - 1 个数有0有9或者前 i - 1 个数无0有9,不能转移得到。
所以 f[i][1][0] = f[i-1][0][0] + f[i-1][1][0]*9
对于前i个数无0有9的 f[i,1,0]——
如果前i-1个数的序列无0无9,则第i个数一定是9;
如果前i-1个数的序列无0有9,则第i个数可取1~9;
如果前i-1个数有0有9或者前i-1个数有0无9,不能转移得到。
所以 f[i][0][1] = f[i-1][0][0] + f[i-1][0][1]*9
再考虑前i个数无0无9,只能由前 i - 1 个数的无0无9的序列转移得到,第 i 个数可取1~8
所以 f[i][0][0] = f[i-1][0][0]*8
所以我们需要将f[i,0,0],f[i,1,0],f[i,0,1],f[i,1,1]的转移方程都列出最终答案为 f[n,1,1]。
注意取模。
Code2:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 1e6+10, M = 3010, INF = 0x3f3f3f3f, mod = 1e9+7;
typedef pair<int, int>PII;
int n;
int a[N];
int f[N][2][2]; //f[i,j,k]表示前i个数;j=1表示出现0,j=0表示没有出现0;k=1表示出现9,k=0表示没有出现9
void solve()
{
cin >> n;
f[0][0][0] = 1; //初始时没有数,满足没有0没有9.相当于空序列这一种情况,初始化为1
for(int i = 1; i <= n; i++)
{
f[i][0][0] = f[i-1][0][0]*8 % mod; //前i个数既没有0也没有9时,第i个数可取1~8,前i-1个数也满足无0无9,f[i-1][0][0]*8
f[i][0][1] = (f[i-1][0][0] + f[i-1][0][1]*9)%mod; //前i个数无0有9,以前i-1个数是否出现9为划分依据:如果前i-1个数无0无9,那么第i个数必须为9,f[i-1,0,0];前i-1个数出现9,第i个数可取1~9,f[i-1,0,1]*9
f[i][1][0] = (f[i-1][0][0] + f[i-1][1][0]*9)%mod; //同上
f[i][1][1] = ((f[i-1][1][0] + f[i-1][0][1])%mod + f[i-1][1][1]*10)%mod; //前i个数有0有9:前i-1个数无0有9,第i个数是0,f[i-1,0,1];前i-1个数有0无9,第i个数是9,f[i-1,1,0];前i-1个数有0有9,第i个数可取0~9,f[i-1][1][1]*10
}
cout << f[n][1][1] % mod << endl; //前n个数有0有9
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
D - Redistribution(找规律/DP/组合数学)
题意:
给出一个正整数 S,问有多少序列满足 ≥ 3,且序列和为 s,答案对
+7 取模。
数据范围:
1 ≤ S ≤ 2000
思路1(找规律):
找规律:n = 1,2 1
3 1
4 1
5 1
6 1+1=a[5]+a[3]=2(6,3 3)
7 1+2=a[6]+a[4]=3(7,3 4,4 3)
8 1+3=a[7]+a[5]=4(8,3 5,5 3,4 4)
9 1+4+1=a[8]+a[6]=6(9,3 6,4 5,5 4,6 3,3 3 3)
…………
n a[n] = a[n-1]+a[n-3]
Code1:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 1e6+10, M = 3010, INF = 0x3f3f3f3f, mod = 1e9+7;
typedef pair<int, int>PII;
int n;
int a[N];
void solve()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
if(i < 3)a[i] = 0;
else if(i == 3 || i == 4 || i == 5)a[i] = 1;
else a[i] = (a[i-1] + a[i-3]+mod)%mod;
}
cout << a[n] % mod << endl;
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
思路2(DP):
状态表示:f[i] 表示序列和为 i 时能划分得到的不同序列的个数。最终答案为 f[s]。
集合划分:将 i 划分成成不同序列时,以序列最后一个数为依据进行划分,最后一个数的取值范围为 3~i 。同时考虑当 i >= 3 时,i 本身可以当做一个长度为 1 的序列,所以先初始化 f[i] = 1,再判断如果 i < 3,则 f[i] = 0;否则 f[i] += f[i-k](k = 3,4,……,i)。
注意取模。
Code2:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 1e6+10, M = 3010, INF = 0x3f3f3f3f, mod = 1e9+7;
typedef pair<int, int>PII;
int n;
int f[N]; //f[i]表示序列和为i时能划分得到的序列个数
void solve()
{
cin >> n;
for(int i = 1; i <= n; i++)f[i] = 1; //i本身可以当做一个长度为1的序列,初始化f[i]=1
for(int i = 1; i <= n; i++)
{
if(i < 3)f[i] = 0;
else{
for(int j = 3; j <= i; j++)
f[i] = (f[i] + f[i-j]+mod)%mod; //和为i时,以划分的最后一块的值为依据进行划分,该值为3~i
}
}
cout << f[n] << endl;
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
思路3(组合数学):
题意相当于:有 n 个球,把它们放在 m 个盒子里,可以有空盒,问有多少种划分方法?
先考虑最多能划分为 k = n/3 组,我们只要对长度为 [1,k] 的组进行计算,最后把每组的答案数累加即可。
由于每组最小为 3,所以 k 组最低需要 3*k ,将剩下 n − 3*k 分给 k 个组,组可以为空,这就是个经典的组合数学题——相当于有 n 个球,把它们放在 m 个盒子里,可以有空盒,问问有多少种划分方法?
如果没有空盒,我们考虑隔板法:n 个球总共 n - 1 个隔间,在这里面选 m - 1 个隔间放挡板,将 n个球分成 m 组,即;
如果有空盒呢,我们先多加上 m - 1 个球,总共 n + m - 1 个球,在这 n + m - 1 个球中选 m - 1 个球作为隔板,将 n 个球分成 m 组,即 。
所以答案为
实现:预处理阶乘和阶乘的逆元即可。枚举k从1~n/3。
Code3:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 1e6+10, M = 3010, INF = 0x3f3f3f3f, mod = 1e9+7;
typedef pair<int, int>PII;
int n;
int fac[N],infac[N];
//快速幂
int qmi(int a,int k,int p)
{
int res = 1;
while(k)
{
if(k & 1) res = res*a%p;
k >>= 1;
a = a*a%p;
}
return res;
}
//预处理阶乘和阶乘的逆元
void init()
{
fac[0] = infac[0] = 1;
for(int i = 1; i <= N; i++)
{
fac[i] = fac[i-1]*i%mod;
infac[i] = qmi(fac[i],mod-2,mod)%mod;
}
}
//求组合数C(a,b)=a!/b!*(a-b)!
int C(int a,int b)
{
return fac[a]*infac[b]%mod*infac[a-b]%mod;
}
void solve()
{
init();
cin >> n;
int k = n/3; //最多分为k组
int res = 0;
for(int i = 1; i <= k; i++)
{
res = (res + C(n-2*i-1,i-1)+mod)%mod; //C(n-2i-1,i-1)枚举能分成的组数i从1~k
}
cout << res << endl;
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
E - Dist Max(结论题)
题意:
给出 n 个二维平面中的点,问两点间的最远曼哈顿距离(点(xi,yi)与点(xj,yj)的曼哈顿距离 |xi-xj| + |yi-yj|)。
数据范围:
2 ≤ N ≤ 2 ×
1 ≤ xi,yi ≤
思路:
简单的说,曼哈顿距离的情况很多,但都可以归类为|(xi+yi)−(xj+yj)|,|(xi−yi)−(xj−yj)|。
预处理所有坐标的 x + y 和 x - y 。用最大值-最小值,最后再取最大值即可。
Code:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 1e6+10, M = 3010, INF = 0x3f3f3f3f, mod = 1e9+7;
typedef pair<int, int>PII;
int n;
vector<int>a,b;
void solve()
{
cin >> n;
a.resize(n);
b.resize(n);
for(int i = 0; i < n; i++)
{
int x,y;
cin >> x >> y;
a[i] = x+y;
b[i] = x-y;
}
sort(a.begin(),a.end());
sort(b.begin(),b.end());
cout << max(*a.rbegin() - *a.begin(),*b.rbegin() - *b.begin()) << endl;
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
F - Contrast(思维)
题意:
给出两个非递减序列 a 和 b,问能否重排 b 使得 ai≠bi 。
数据范围:
1 ≤ N ≤ 2 ×
1 ≤ Ai,Bi ≤ N
思路:
将序列 b 反转,因为 a 为非递减序列,b 为非递增序列,所以序列中至多存在一段区间 [l,r] 满足 a[l~r] = b[l~r] = c。
如果区间外的某个 bj 与区间内的 bi 调换后满足题意,那么 aj≠bi, bj≠ai,即 aj≠c 且 bj≠c 。
如果这样的 bj 个数小于区间长度则无解,否则输出调换后的序列 b 即可。
实现:将b数组反转后,遍历a,b数组,找出c;再找出区间[l,r]后,遍历b数组找可以替换的值。如果最终有值找不到可以替换的值,无解。
Code:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 1e6+10, M = 3010, INF = 0x3f3f3f3f, mod = 1e9+7;
typedef pair<int, int>PII;
int n;
vector<int>a,b;
void solve()
{
cin >> n;
a.resize(n),b.resize(n);
for(int i = 0; i < n; i++)cin>>a[i];
for(int i = 0; i < n; i++)cin >> b[i];
reverse(b.begin(),b.end()); //将b数组逆序
int c = -1;
for(int i = 0; i < n; i++)
{
if(a[i] == b[i])
{
c = a[i]; //找到两序列重合的那段区间的数c
break;
}
}
int l = n,r = -1;
for(int i = 0; i <n; i++)
{
if(a[i] == c && b[i] == c)
{
l = min(l,i); //l为该区间的左端点
r = max(r,i); //r为该区间的右端点
}
}
for(int i = 0;i < n; i++)
{
if(a[i]!=c && b[i]!=c && l <= r)
{
swap(b[i],b[l]); //遍历b数组,找能与区间替换的数
l++;
}
}
if(l <= r)cout << "No" << endl; //l<=r说明遍历完了b数组,找不到可以替换的了
else
{
cout << "Yes" << endl;
for(int i = 0; i < n; i++) cout << b[i] <<' ';
cout << endl;
}
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
吐槽:这次的题基本上都是思维题。
题目注意点:
1.C题:的容斥原理运用不太熟练,当时写时迷糊了一会才搞对(太菜了)。下次再遇见可以考虑画图。
2.D题给我的收获满大的,首先实在没思路就找规律的思维要有;这题DP也是很基础的DP,正好给好久没写dp的我回忆回忆dp;然后是组合数学的方法,有没有空盒这块犹记得高中几何课学过,但是忘记了,也要捡起来捡起来。
3.E题是个结论题,记一下记一下。
4.F题是个纯思维题,一看题解恍然大悟╰(*°▽°*)╯。