基础
CF1420C1 Pokémon Army (easy version)
Example
input
3
3 0
1 3 2
2 0
1 2
7 0
1 2 5 4 3 6 7
output
3
2
9
Note
In third test case we can build an army in such way: [1 2 5 4 3 6 7], its strength will be 5−3+7=9.
解题思路:波峰就加波谷就减,记得ans开 long long
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e5 + 5;
int t,n,k;
int a[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> t;
while (t--)
{
memset(a, 0, sizeof(a));
cin >> n>>k;
for (int i = 1; i <= n; i++)
cin >> a[i];
ll ans = 0;
for (int i = 1; i <= n; i++)
{
//ans += max(0, a[i] - a[i + 1]);
if(a[i]>a[i-1]&&a[i]>a[i+1]) ans+=a[i];
if(a[i]<a[i-1]&&a[i]<a[i+1]) ans-=a[i];
//cout<<ans<<" ";
}
cout << ans << endl;
}
return 0;
}
CF1420C1 Pokémon Army (hard version)
思想类似
我们可以发现,这是一个一加一减的形式,然后我看几个数据,可以发现——
每次的加都是一段递增数列的最后一项,每次减都是一段递减数列的最后一项,也就是一段递增的第一项。
这个事情还是非常好证明的,用贪心就可以证出。
如果这样求加减的话,那这道题就简单了。
证明如果ai>ai+1
,那么说明这是一段递增的数列,那么不断的加上,最后可以得到一段中的amax-amin,就是我们刚刚得到的结论。
交换上只需把原本的交换位置上对答案的贡献减掉,再把交换后对答案的贡献加上即可。
#include<bits/stdc++.h>
#define N 300005
using namespace std;
long long tmp,l,r,taxi,n,a[N],ans,q;
int main(){
scanf("%lld",&taxi);
for(register int ttt=1;ttt<=taxi;ttt++)
{
scanf("%lld%lld",&n,&q);
a[n+1]=0,a[0]=0,ans=0;
for(register int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(register int i=0;i<=n;i++)
{
if(a[i]>=a[i+1])ans+=(a[i]-a[i+1]);
}
printf("%lld\n",ans);
for(register int i=1;i<=q;i++)
{
scanf("%lld%lld",&l,&r);
if(a[l-1]>=a[l])ans-=(a[l-1]-a[l]);
if(a[l]>=a[l+1])ans-=(a[l]-a[l+1]);
if(a[r-1]>=a[r]&&r-1!=l)ans-=(a[r-1]-a[r]);
if(a[r]>=a[r+1])ans-=(a[r]-a[r+1]);
swap(a[l],a[r]);
if(a[l-1]>=a[l])ans+=(a[l-1]-a[l]);
if(a[l]>=a[l+1])ans+=(a[l]-a[l+1]);
if(a[r-1]>=a[r]&&r-1!=l)ans+=(a[r-1]-a[r]);
if(a[r]>=a[r+1])ans+=(a[r]-a[r+1]);
printf("%lld\n",ans);
}
}
return 0;
}
design problem(二分)
链接:https://ac.nowcoder.com/acm/problem/211817
来源:牛客网
题目描述
输出描述:
Only one line containing one integer, denoting the maximum number of problemsets you can make.
示例1
输入
4 10 12
4 20 10 6
1 2
3 5
2 4
0 2
输出
4
说明
One possible scheme is (1, 5, 2, 2), (1, 5, 2, 2), (1, 5, 3, 1), (1, 5, 3, 1)_{}(1,5,2,2),(1,5,2,2),(1,5,3,1),(1,5,3,1)
.
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
typedef long long ll;
typedef unsigned long long ull;
struct node{
ll s,li,ri;
};
node a[N];
ll n,l,r;
bool check(ull m){//测试当前分组数是否可行
if(m==0) return true;
ull sum = 0;
ull minn = 0;
for(int i = 1;i<=n;i++)
{
minn += a[i].li;
if(a[i].s<m*a[i].li) return false; //每组都取左端点都不够,return f
sum +=min( (a[i].ri -a[i].li )*m , a[i].s -a[i].li*m );//每一个问题库还有一个范围
//所以 问题库/分组 的值最大也不能超过他自身的右端点r,多余的部分就可以用
}
if(minn + sum/m>=l && minn<=r) return true;
else return false;
}
int main()
{
cin>>n>>l>>r;
for(int i = 1;i<=n;i++) cin>>a[i].s;
for(int i = 1;i<=n;i++) cin>>a[i].li>>a[i].ri;
ll s = 0,t = 1e18;//分组数至少到1e18
while(s<=t){//二分 分组数
ull m = (s+t)/2;
if(check(m)==true){
s = m+1;
}
else t = m-1;
}
cout<<t;
}
注意:全使用ll 时只能过70%
所以要用unsigned long long
递增数组
给你一个数组a,其中所有ai是整数且大于0。
在一个操作中,您可以选择两个不同的索引i和j(1<=i,j<=n)。如果gcd(ai,aj)等于整个数组a的最小元素,则可以交换ai和aj
gcd(x,y)表示整数x和y的最大公约数。
现在,你想多次使用该操作将a数组变为非递减序列。
输入格式:
第一行包含一个整数n(1<=n<=10^5)-数组a的长度。
第二行包含n个正整数a
ai<=10^9)—数组本身。
输出格式:
如果可以使用所描述的操作使数组不递减,则输出“ YES”,否则,则输出“ NO”。
输入样例1:
6
4 3 6 6 2 9
输出样例1:
YES
样例解释:
第一次操作选择第三个数和第五个数 -> 4 3 2 6 6 9
第二次操作选择第一个数和第三个数 -> 2 3 4 6 6 9
即一共花费两次操作即可将序列变为非递减序列
输入样例2:
5
7 5 2 2 4
输出样例2:
NO
看到题目的第一想法就是,先将每个数字排序,找到该数字转换结束后应该在的位置,判断 数字是否能通过换位操作换到该位置。
我们称排序后数字所在的位置为该数字的正确位置。 若想将某位于非正确位置的数字换到正确位置,当且仅当该数字和处于其正确位置的数字均 为序列最小值的倍数(他们与序列最小值的gcd为序列最小值本身)。
推广一下即可得到,当且仅当所有处于非正确位置的数字均为序列最小值的整数倍,该序列 可以通过使用不超过2n次操作将序列变为非递减序列。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int minn=0x3f3f3f3f;
int main()
{
int n;
cin>>n;
vector<int> a(n);
for(int i = 0;i < n;++i){
cin>>a[i];
if(a[i]<minn) minn=a[i];
}
vector<int> b(a);
sort(b.begin(),b.end());
int ok = 1;
for(int i = 0;i < n;++i){
if(a[i] != b[i] && a[i] % minn!=0){
ok = 0;
break;
}
}
if(ok) puts("YES");
else puts("NO");
return 0;
}
CF1409D Decrease the Sum of Digits(循环)
显然只有在进位时才会减小 n,所以我们只要枚举能够使 n 进位的数就 ok 了。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t;
ll n,s,ans;
int count(ll x)//数位和
{
int ans=0;
while(x)
{
ans+=x%10;
x/=10;
}
return ans;
}
int countn(ll x)//数位
{
int ans=0;
while(x)
{
ans++;
x/=10;
}
return ans;
}
ll work(ll n , ll s) {
if(count(n) <= s)return 0;
ll k = 1 , cnt = 0 , t = n;
while(t) cnt++ , t /= 10;
for(ll i = 1; i <= cnt; i++) {
k *= 10;
if(count(k - n % k + n) <= s)
return k - n%k;
}
}
int main()
{
cin>>t;
while(t--)
{
cin>>n>>s;
cout<<work(n,s)<<endl;
}
return 0;
}
Minimal Height Tree(贪心)
题目大意:给定BFS序列,一层的BFS顺序按节点编号递增,球树的最小高度
思路:记 a i+1 < a i
为冲突事件,对于这种事件要么挂在上层另一个没挂过点的节点下,要么新开一层,把 i+1 挂在这层节点上
所以能挂就挂,遇到冲突就消耗一个上层节点,消耗完就新开一层
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10;
int t,n;
int a[N];
int main()
{
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans=1,last=2,sum=0;
for(int i=2;i<n;i++){
if(a[i+1]<a[i]){
if(--sum<0) ans++,sum=i-last,last=i+1;
}
}
cout<<ans<<endl;
}
return 0;
}
数据结构
easy equation
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e8+10;
ll f[N],D[N];
int main()
{
ll a,b,c,d;
cin>>a>>b>>c>>d;
for(int i=0;i<=a;i++)
{
f[i]++;
f[i+b+1]--;
}
for(int i=1;i<=a+b;i++) f[i]+=f[i-1];
for(int i=0;i<=a+b;i++)
{
D[i]+=f[i];
D[i+c+1]-=f[i];
}
for(int i=1;i<=a+b+c;i++) D[i]+=D[i-1];
ll ans=0;
for(int i=0;i<=d;i++) ans+=D[i];
cout<<ans<<endl;
return 0;
}
Perform Easily(队列)
输入输出样例
输入 #1
1 4 100 10 30 5
6
101 104 105 110 130 200
输出 #1
0
输入 #2
1 1 2 2 3 3
7
13 4 11 12 11 13 12
输出 #2
7
1 题意
有六根弦,每根弦的基础音高是ai
如果你在第 i 根弦的第 j 个位置上弹奏,你能得到一个音高为 ai+j 的音。
你现在需要演奏所有音,每个音可以在任意一个弦的任意一个位置上弹奏。求所有位置中最大值减最小值的最小值。
2 思路
我们对于每个音求合法位置,并且记录在一个位置上通过某种弦可以弹第 x 个音符。
然后从小到大排序位置,左右跑一遍双指针即可。
具体来说,我们维护一个队列,如果最左边的位置对应的音符已经出现两次,则出队。每次进队一个并不断出队,更新答案。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100003;
const int M=600003;
int a[13],b[N],n,l=1,r=1,cnt=0;
pair<int,int> x[M];
int f[N];
signed main()
{
for(int i=1; i<=6; i++) cin>>a[i];
n=read();
for(int i=1; i<=n; i++) cin>>b[i];
for(int i=1; i<=6; i++) for(int j=1; j<=n; j++) x[++cnt]=make_pair(max(b[j]-a[i],1ll),j);
long long ans=1000000000000ll;
sort(x+1,x+cnt+1);
int now=0;
for(int l=1,r=1; r<=cnt; r++)
{
if(f[x[r].second]==0) ++now;
++f[x[r].second];
while(f[x[l].second]>1) --f[x[l].second],++l;
if(now==n) ans=min(ans,x[r].first-x[l].first);
}
cout<<ans<<endl;
return 0;
}
字符串
CF1430E String Reversal(树状数组求逆序对)
题目描述
You are given a string ss . You have to reverse it — that is, the first letter should become equal to the last letter before the reversal, the second letter should become equal to the second-to-last letter before the reversal — and so on. For example, if your goal is to reverse the string “abddea”, you should get the string “aeddba”. To accomplish your goal, you can swap the neighboring elements of the string.
Your task is to calculate the minimum number of swaps you have to perform to reverse the given string.
输入格式
The first line contains one integer n ( 2 ≤n≤200000 ) — the length of s .
The second line contains ss — a string consisting of nn lowercase Latin letters.
输出格式
Print one integer — the minimum number of swaps of neighboring elements you have to perform to reverse the string.
题意翻译
给定一个长度为n的字符串A,B为翻转后的A字符串,请你求出B最少需要多少次相邻字符互换操作才能与A完全一致。
输入输出样例
输入 #1
5
aaaza
输出 #1
2
输入 #2
6
cbaabc
输出 #2
0
输入 #3
9
icpcsguru
输出 #3
30
说明/提示
In the first example, you have to swap the third and the fourth elements, so the string becomes “aazaa”. Then you have to swap the second and the third elements, so the string becomes “azaaa”. So, it is possible to reverse the string in two swaps.
Since the string in the second example is a palindrome, you don’t have to do anything to reverse it.
Solution
最小交换次数,交换相邻想到逆序对。
假设原串每个字符的编号为 1-n1−n,然后我们将反串每个字符的位置按升序求出来,我们就求出了最优情况下每个字符应该变换到的位置,比如第一个样例aaaza对应的序列应该是 1,3,4,2,51,3,4,2,5,交换相邻两数,使得数列有序
把原串反转后的串的第一个字符到最后一个字符依次给予一个递增的权值,然后再传递给原串相应的位置那个权值,
那原串的反串的各个位的权值可以 1 到 n,这个已经确定了,怎么传递给原串对应字符呢?相同的字符传递给谁更优呢?
因为求的是逆序对个数,所以原串相同字符的权值也递增会使答案更小。
这样就比较简单了,处理一下每个字符的权值然后逆序对即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+ 5;
vector<int> vec[30];
char a1[N],a2[N];
ll tree[N<<1],ans;
int t[N],cnt[N];
int n;
inline int lowbit(int x) { return x & (-x); }
ll sum(int x) { ll rsum = 0; for(; x; x -= lowbit(x)) rsum += tree[x]; return rsum; }
void modify(int x, ll k) { for(; x <=n; x += lowbit(x)) tree[x] += k; }
int main()
{
char s;
cin>>n;
for(int i=1;i<=n;i++){
cin>>s;
a1[i]=s;a2[n-i+1]=s;
}
for(int i=1;i<=n;i++){
vec[a2[i]-'a'].push_back(i);
}
for(int i=1;i<=n;i++){
int now=a1[i]-'a';
t[i]=vec[now][cnt[now]++];
}
for(int i=1;i<=n;i++){
modify(t[i], 1ll);
ans+=i-sum(t[i]);
}
cout<<ans<<endl;
return 0;
}
原理:树状数组的功能就是可以单点更新,区间查询,这样你把每个数该在的位置离散化出来,然后每次把每个数该在的位置上加上1,比如一个数该在的位置为x,那么用add(x)把这个位置加上1,然后再用区间查询read(x)查询1~x的和,也就是可以知道前面有多少个数是比他小的了(包括那个数自己),再用已经插入的数的个数减去read(x),就算出了前面有多少个数比他大了。
构造回文数 (高精度加法)
回文数是一种很有趣的数,对于一个不是回文数的数,我们可以通过以下操作来将其变成回文数:将数反转,加到原数上,重复这个过程,直到得到回文数为止。
输入格式:
输入1个数位不超过1000的正整数。
输出格式:
对每一个输入,输出将其变成回文数的过程。每一行按以下个数输出:
A + B = C
A是原数,B是反转数,C是和,重复这个过程,直到C是回文数为止。在最后一行输出C is a palindromic number.。为了控制循环次数,我们规定,如果经过10次操作,还得不到回文数,那么停止,显示Not found in 10 iterations.
输入样例1:
在这里给出一组输入。例如:
1234
输出样例1:
在这里给出相应的输出。例如:
1234 + 4321 = 5555
5555 is a palindromic number.
输入样例2:
在这里给出一组输入。例如:
1239102349120349
输出样例2:
在这里给出相应的输出。例如:
1239102349120349 + 9430219432019321 = 10669321781139670
10669321781139670 + 07693118712396601 = 18362440493536271
18362440493536271 + 17263539404426381 = 35625979897962652
35625979897962652 + 25626979897952653 = 61252959795915305
61252959795915305 + 50351959795925216 = 111604919591840521
111604919591840521 + 125048195919406111 = 236653115511246632
236653115511246632 + 236642115511356632 = 473295231022603264
473295231022603264 + 462306220132592374 = 935601451155195638
935601451155195638 + 836591551154106539 = 1772193002309302177
1772193002309302177 + 7712039032003912771 = 9484232034313214948
Not found in 10 iterations.
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 10;
bool check(string s){//回文的判断
for(int i = 0;i < s.size()/2;++i){
if(s[i] != s[s.size()-i-1]) return 0;
}
return 1;
}
int t[maxn];//用于存储计算后的数值
string add(string a, string b)//高精度加法
{
//逆置两数便于计算
reverse(a.begin(),a.end());//string中的反转函数
reverse(b.begin(),b.end());
if(a.size() < b.size()) swap(a,b);
//存入数组处理进位
int n = a.size(),m = b.size();
for(int i = 0;i < m;++i) t[i] = a[i] - '0' + b[i] - '0';
for(int i = m;i < n;++i) t[i] = a[i] - '0';
for(int i = 0;i < n;++i){
if(t[i] < 10) continue;
t[i+1] += t[i] / 10;
t[i] = t[i] % 10;
}
//输出到结果字符串
string res;
if(t[n] != 0) res.push_back(t[n] + '0');//最高位未进位
for(int i = n-1;i >= 0;--i) res.push_back(t[i]+'0');
return res;
}
int main()
{
string s;
cin>>s;
for(int i = 0;i < 10;++i){
if(check(s)){
cout<< s << " is a palindromic number.\n";
return 0;
}
string t(s);//s赋值给t
reverse(t.begin(),t.end());
string sum = add(s,t);
cout<< s << " + " << t << " = " << sum<<endl;
s = sum;
}
if(check(s)) cout<< s << " is a palindromic number."<<endl;
else cout<<"Not found in 10 iterations."<<endl;
return 0;
}
搜索
子序列(dfs)
给出一个长度为n的正整数序列,问是否存在一个非空子序列的所有元素和为w
输入格式:
第一行为两个正整数n,w(1<=n<=20,0<=w<=10
9
).
第二行包括n个正整数a,即序列中的所有元素.
输出格式:
若存在和为w的子序列,输出"YES",否则输出"NO"(不包含双引号)。
输入样例:
4 10
2 20 5 3
输出样例:
YES
#include <bits/stdc++.h>
using namespace std;
const int N=25;
int a[N];
int n,w,flag;
void dfs(int now,int sum,int num)//num选择的数的个数
{
if(sum == w && num) flag=1;
if(now == n || sum>w) return;
dfs(now+1,sum+a[now],num+1);//两个分支
dfs(now+1,sum,num);
return ;
}
int main()
{
cin>>n>>w;
for(int i=0;i<n;i++) cin>>a[i];
dfs(0,0,0);
if(flag) cout<<"YES";
else cout<<"NO";
return 0;
}
Walking Machine
从边界向内找,如果能够从边界某点进入迷宫,则能到达该点的点满足条件
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1010;
typedef pair<int,int> PII;
char mp[N][N];
int n,m,ans;
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
queue<PII> q;
void bfs()
{
while(q.size())
{
int x=q.front().first,y=q.front().second;
q.pop();
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<1||a>n||b<1||b>m) continue;
if(i==0&&mp[a][b]=='A') q.push({a,b}),ans++;
else
if(i==1&&mp[a][b]=='D') q.push({a,b}),ans++;
else
if(i==2&&mp[a][b]=='S') q.push({a,b}),ans++;
else
if(i==3&&mp[a][b]=='W') q.push({a,b}),ans++;
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>mp[i][j];
for(int i=1;i<=m;i++) q.push({0,i}),q.push({n+1,i});
for(int i=1;i<=n;i++) q.push({i,-0}),q.push({i,m+1});
bfs();
cout<<ans<<endl;
return 0;
}
character wheels
一个矩阵从外到内分别为第一层到第n/2
层,现在有m次操作,L/R x y表示将第x层旋转(逆时针/瞬时针)90度,操作P为询问现在矩阵的样子。
那么我们只需要知道坐标(i,j)经过旋转可以到达那里,那么我们带入几个数据进去算就知道顺时针旋转90度就是( i , j ) → ( j , n − i + 1 ) 逆时针转一次就是顺时针转3次
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=55;
char mp[N][N],tmp[N][N];
int n;
void print()
{
for (int i=1; i<=n; i++)
printf ("%s\n",mp[i]+1);
}
PII turn(int x,int y)
{
return {y,n-x+1};
}
void solve(char dir,int r,int nb)
{
if (dir=='L') nb=4-nb;
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
tmp[i][j]=mp[i][j];
for (int i=r; i<=n-r+1; i++){
PII now={r,i},nowbo={n-r+1,i};
for (int j=1; j<=nb; j++){
now=turn(now.first,now.second);
nowbo=turn(nowbo.first,nowbo.second);
}
tmp[now.first][now.second]=mp[r][i];
tmp[nowbo.first][nowbo.second]=mp[n-r+1][i];
}
for (int i=r+1; i<=n-r; i++){
PII now={i,r},nowbo={i,n-r+1};
for (int j=1; j<=nb; j++){
now=turn(now.first,now.second);
nowbo=turn(nowbo.first,nowbo.second);
}
tmp[now.first][now.second]=mp[i][r];
tmp[nowbo.first][nowbo.second]=mp[i][n-r+1];
}
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
mp[i][j]=tmp[i][j];
}
int main(int argc, char const *argv[])
{
scanf ("%d",&n);
for (int i=1; i<=n; i++)
scanf ("%s",mp[i]+1);
int m;
scanf ("%d",&m);
while (m--){
char op[10];
scanf ("%s",op);
if (op[0]=='P') print();
else {
int r,nb;
scanf ("%d%d",&r,&nb);
nb%=4;
solve(op[0],r,nb);
}
}
return 0;
}
数学
CF1426F Number of Subsequences
题目描述
You are given a stri
ng ss consisting of lowercase Latin letters “a”, “b” and “c” and question marks “?”.
Let the number of question marks in the string ss be kk . Let’s replace each question mark with one of the letters “a”, “b” and “c”. Here we can obtain all 3^{k}3
k
possible strings consisting only of letters “a”, “b” and “c”. For example, if s =s= “ac?b?c” then we can obtain the following strings: [[ “acabac”, “acabbc”, “acabcc”, “acbbac”, “acbbbc”, “acbbcc”, “accbac”, “accbbc”, “accbcc” ]] .
Your task is to count the total number of subsequences “abc” in all resulting strings. Since the answer can be very large, print it modulo 1e9
A subsequence of the string tt is such a sequence that can be derived from the string tt after removing some (possibly, zero) number of letters without changing the order of remaining letters. For example, the string “baacbc” contains two subsequences “abc” — a subsequence consisting of letters at positions (2, 5, 6)(2,5,6) and a subsequence consisting of letters at positions (3, 5, 6)(3,5,6) .
输入格式
The first line of the input contains one integer nn (3 \le n \le 200,000)(3≤n≤200000) — the length of ss .
The second line of the input contains the string ss of length nn consisting of lowercase Latin letters “a”, “b” and “c” and question marks"?".
输出格式
Print the total number of subsequences “abc” in all strings you can obtain if you replace all question marks with letters “a”, “b” and “c”, modulo 10^{9} + 710
9
+7 .
题意翻译
给定一个含有abc?的字符串,?可能是abc中的任意一个,求所有可能的无?字符串中,子序列abc出现的次数.
输入输出样例
输入 #1
6
ac?b?c
输出 #1
24
输入 #2
7
???????
输出 #2
2835
输入 #3
9
cccbbbaaa
输出 #3
0
输入 #4
5
a???c
输出 #4
46
说明/提示
In the first example, we can obtain 9 strings:
“acabac” — there are 22 subsequences “abc”,
“acabbc” — there are 44 subsequences “abc”,
“acabcc” — there are 44 subsequences “abc”,
“acbbac” — there are 22 subsequences “abc”,
“acbbbc” — there are 33 subsequences “abc”,
“acbbcc” — there are 44 subsequences “abc”,
“accbac” — there is 11 subsequence “abc”,
“accbbc” — there are 22 subsequences “abc”,
“accbcc” — there are 22 subsequences “abc”.
So, there are 2 + 4 + 4 + 2 + 3 + 4 + 1 + 2 + 2 = 242+4+4+2+3+4+1+2+2=24 subsequences “abc” in total.
解题思路:一开始的解题思路是遍历b 或者? 找到他左边的a 右边的c ,将?视作a或c,然后每个c求组合数,后发现一个求法不能囊括所有情况,故分情况讨论
没开long long 又wa了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10,mod=1e9+7;
ll n,ans,c,r,a,l;
char s[N];
ll pow3[N];
int main()
{
cin>>n;
cin>>s;
for(int i=0;i<n;i++)
{
if(s[i]=='c') c++;
if(s[i]=='?') r++;
}
pow3[0] = 1;//预处理3的n次方
for (int i= 1; i < n; i++) {
pow3[i] = pow3[i-1] * 3 % mod;
}
for(int i=0;i<n;i++){
char b=s[i];
if (b == 'a') {
a++;
} else if (b == 'c') {
c--;
} else {
if(b == '?' ) {
r--;
}
ans += a * c % mod * pow3[l+r] % mod;
if (l > 0) {
ans += l * c % mod * pow3[l-1+r] % mod;
}
if (r > 0) {
ans += a * r % mod * pow3[l+r-1] % mod;
}
if(l > 0 && r > 0) {
ans += l * r % mod * pow3[l+r-2] % mod;
}
if(b == '?'){
l++;
}
}
}
cout<<ans%mod;
return 0;
}
稳定序列(最大公因数+组合数)
取模运算在计算机与数论中存在着很广泛的应用,我们定义 xmody 运算为x除以y的余数。
对于一个数组, 若其满足以下条件,则我们称其为稳定数组。
(p数组为任意(1-n)的不重复排列)
即对于每个非负整数x,我们对数组a中的元素重新排序之后
的值不会发生改变。
那么给出两个正整数n和k,请计算稳定序列满足 1≤a≤n的个数
输入格式:
输入包括一行,包括两个整数n和k (1≤n,k≤5⋅105)
输出格式:
输出包括一个数字,即满足条件的稳定序列的个数,因为结果可能过大,所以将结果对998244353取模。
输入样例1:
1 1
输出样例1:
1
输入样例2:
1337 42
输出样例2:
95147305
解题思路
满足题设条件,很容易得到当且仅当 的最大公因数为a序列中最小值时,无论 顺序如何排列,其结果均不会发生改变。 这样,我们只需将1-n每个数进行枚举,枚举 作为最大公因数时,存在多少种情况能选择 个数字均为该数的倍数 。即C(n的倍数个数,k-1)。 因为要对结果进行取模,所以求组合数时,先预处理阶乘再费马小定理求逆元即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+10,mod=998244353;
ll fact[N],infact[N];
ll qmi(int a,int k,int p)
{
int res=1;
while(k)
{
if(k&1) res=(ll)res*a%p;
k>>=1;
a=(ll)a*a%p;
}
return res;
}
ll C(ll a,ll b){
if(a<b) return 0;
return (ll)fact[a]*infact[b]%mod*infact[a-b]%mod;
}
int main()
{
fact[0]=1,infact[0]=1;
for(int i=1; i<N; i++)//预处理
{
fact[i]=(ll)fact[i-1]*i%mod;
infact[i]=(ll)infact[i-1]*qmi(i,mod-2,mod)%mod;
}
int n,k;
cin>>n>>k;
ll res = 0;
for(int i = 1;i <= n;++i){
ll temp = C(n/i-1,k-1);
res = (temp + res) % mod;
}
cout<<res<<endl;
return 0;
}
CF1420D Rescue Nibel!(组合数+离散化)
有 n 条线段
你需要从中选出 k条令他们的交集不为空,求方案数对 998244353 取模的结果。
输入输出样例
输入 #1
7 3
1 7
3 8
4 5
6 7
1 3
5 10
8 9
输出 #1
9
输入 #2
3 1
1 1
2 2
3 3
输出 #2
3
输入 #3
3 2
1 1
2 2
3 3
输出 #3
0
输入 #4
3 3
1 3
2 3
3 3
输出 #4
1
输入 #5
5 2
1 3
2 4
3 5
4 6
5 7
输出 #5
7
题目大意:给出 n 盏灯,每盏灯在 [ l , r ] 这段时间内会保持点亮的状态,问恰好有 k 盏灯同时点亮,有多少种组合方式
题目分析:因为每盏灯都有一个开始亮的时间和一个熄灭的时间,不难想到用差分去维护每盏灯点亮的时间,不过如果是对于每个时间戳去计算贡献的话,很容易重复计算,因为在不同的时间戳上,如果选择的 k 盏灯相同的话,是不会重复计算贡献的,但是每盏灯都有一个开始亮的时间,也就是 l[ i ],所以我们不妨从这里入手
对于某个时间戳来说,此时如果有 sum 个灯是亮着的状态,有 start 个灯是在当前时间戳才开始亮的状态,如果是直接 C( sum , k ) 选 k 个灯的话,有可能选出来的 k 个灯都不属于 start 个灯中任意一个,所以需要减去这种情况,也就是减去 C( sum - start , k ) ,对于每个时间戳都计算一下贡献,最后就是答案了
需要注意的是,虽然 n 是 3e5 的,但每个灯都有一个 l 和一个 r ,最后离散化后的数组最大可以到达 6e5 的样子,所以差分数组一定要开大一点
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
const int mod=998244353;
ll fac[N],inv[N];
int n,k,l[N],r[N],st[N],delta[N];
vector<int>node;
ll q_pow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void init()
{
fac[0]=1;
for(int i=1;i<N;i++)
fac[i]=fac[i-1]*i%mod;
inv[N-1]=q_pow(fac[N-1],mod-2);
for(int i=N-2;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(int n,int m)
{
if(n<m)
return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int get_num(int x)
{
return lower_bound(node.begin(),node.end(),x)-node.begin()+1;
}
void discreate()//差分维护每盏灯点亮的时间
{
sort(node.begin(),node.end());
node.erase(unique(node.begin(),node.end()),node.end());
for(int i=1;i<=n;i++)
{
st[get_num(l[i])]++;
delta[get_num(l[i])]++;
delta[get_num(r[i])+1]--;
}
}
int main()
{
init();//预处理
cin>>n>>k;
for(int i=1;i<=n;i++)
{
scanf("%d%d",l+i,r+i);
node.push_back(l[i]);
node.push_back(r[i]);
}
discreate();
ll ans=0;
for(int i=1;i<=node.size();i++)
{
delta[i]+=delta[i-1];
ans=(ans+C(delta[i],k)-C(delta[i]-st[i],k)+mod)%mod;
}
cout<<ans;
return 0;
}