cf 627
https://blog.csdn.net/qq_43408238/article/details/104734754
给你一堆数据,数据之间两两相加再异或求答案
D
education cf 83
题意:n个数的范围是[1,m],能构造出多少个序列只有一对相同的,且存在一个位置i,i前递增,i后递减;
分析:m中选n-1个数,最大的放中间,剩下的放在左右,再多一个重复的,但不能等于最大;
思路:由于必须只有一对相同,所以我们只能从m个里面拿出n-1个出来,那就是c(n-1,m)种组合
再接下来我们将数值最大的放中间,然后就可以开始两个相等的放这个最高的两边,那么我们就剩下n-2个数字给我们选择,这n-2个我们就可以随便左右放了,所以就2的(n-2)次方
最后就是在这个相同的数字实在n-1个数字里选的,也就是我们可以选择n-1个不同的作为重复的数字
#include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; const int maxn=2e5+7; const int mod=998244353; const ll INF=1e18; int n,m; ll fac[maxn]; ll qpow(ll x,int n) { ll res=1; while(n) { if(n&1) res=(res*x)%mod; x=(x*x)%mod; n>>=1; } return res; } ll inv(ll x){return qpow(x,mod-2);} int main() { fac[0]=1; scanf("%d%d",&n,&m); for(int i=1;i<=m+1;i++) fac[i]=fac[i-1]*i%mod; if(n==2) puts("0"); else printf("%lld\n",(1ll*n-2)*fac[m]%mod*inv(fac[n-1])%mod*inv(fac[m-n+1])%mod*qpow(2ll,n-3)%mod); }
题意:
每次可以选择相邻的两个相等的数,将其合并为一个,并且值加一。
可以执行若干次上述操作,问最后最少剩下多少个数。
思路:
直接的想法就是贪心进行合并,但是向左、向右合并的情况太过于复杂,所以我们需要考虑dp dp
。
注意到一段连续相等的数,若个数为奇数个,该操作就相当于会给其一个划分,然后左右两边各自合并(j基数肯定存在一个无法被合并,所以我们就无论他是基数偶数我们都进行划分比较因为你直接计算这个区间相同的话肯定不一定是最优的,他可以有中间隔开
计算由这个中间隔开的两边,因为你要取最优肯定是那个不能被合并的隔开两边而不是一条连续合并到底,这样两边还有可能合并其他数字)。那么每个各自合并的部分都可以看作是向左或是向右在合并。
所以我们求出f[l,r] f[l,r]
表示[l,r] [l,r]
区间向左合并的最小可能数。
然后dp dp
一遍枚举区间划分即可。
细节见代码:
#include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; const int maxn=2e5+7; const int mod=1e9+7; const ll INF=1e18; int n,a[maxn],dp[1010][1010]; int solve(int l,int r) { stack<int> st; for (int i=l;i<=r;++i) { int tmp=a[i]; while((int)st.size() && st.top()==tmp) { st.pop(); ++tmp; } st.push(tmp); } return (int)st.size(); } void rua() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int len=1;len<=n;++len) for(int l=1;l+len-1<=n;++l) { int r=l+len-1; dp[l][r]=solve(l,r); for(int k=l;k<r;++k) dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]); } printf("%d\n",dp[1][n]); } int main() { rua(); return 0; }