给定N、A、B,构造一个长度为N的排列。
使得其最长上升子序列长度为A,最长下降子序列长度为B
n<=
观察N=AB的情况,可以发现是 3 2 1 6 5 4 9 8 7 3_{2_1}^{{6_{5_4}}^{9_{8_7}}} 321654987 这样子的,组数是A,个数是B。
于是AB
这题重点并不是怎么想而是怎么写!!!!!这种模拟题有一种常见的减少思维量的技巧,就是一边做一边 n − − n-- n−−,用剩下的 n n n去思考可以省事很多。(一位代码写炸的人的自我反省)
#include
#define maxn 100005
#define MAXN 1000005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
LL read(){
LL res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=c^48;
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
int n,a,b,T;
int main(){
T=read();
while(T--){
n=read(); a=read(); b=read();
if(n>a*b || n
visit_world 有一个商店,商店里卖N个商品,第i 个的价格为a[i]。我们称一个正整数K 是美妙的,当且仅当我们可以在商店里选购若干个商品,使得价格之和落在区间 [K,2K]中。
问:有多少个美妙的数。
考虑一个价格 a i a_i ai它能影响到 [ a i + 1 2 , a i ] [\frac{a_i+1}{2},a_i] [2ai+1,ai]假如现在有了n个答案区间,那么每个答案区间的右端点+ a i a_i ai就是更新区间了。这样其实这样就能做了,但是我们继续将这道奇技淫巧。
假如之前的区间连续。首先分类讨论a[i],设现在处理到了 r r r表示最右端点。
如果 a i a_i ai< r r r, r > ( r + a i ) / 2 r>(r+a_i)/2 r>(r+ai)/2所以新加一个 a i a_i ai的时候直接 a n s + = a i ans+=a_i ans+=ai就行了,也就是突出来的长度。
如果 a i > r a_i>r ai>r,那么 r < ( r + a i ) / 2 r<(r+a_i)/2 r<(r+ai)/2,也就是会空出来一个长度,剪掉就行。
如果不连续,首先那个区间的右端点一定是某个前缀和设 a + b a+b a+b,那么下一段的开始假设是 ( a + b + c ) / 2 (a+b+c)/2 (a+b+c)/2,那么新的靠左区间的右端点是 a + b + x / 2 a+b+x/2 a+b+x/2下一个是 ( a + b + c ) / 2 (a+b+c)/2 (a+b+c)/2大,碰到时 a + b + x / 2 < ( a + b + c ) / 2 a+b+x/2<(a+b+c)/2 a+b+x/2<(a+b+c)/2即 a + b + x < c a+b+x<c a+b+x<c那么我们只要让 x > c x>c x>c就可以保证永远碰不到了。那么就 a i a_i ai排序(可以理解成一跑跑太远)
#include
#define maxn 100005
#define MAXN 1000005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
LL read(){
LL res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=c^48;
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
int n,m,a[maxn];
LL sum[maxn];
LL ans;
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) sum[i]=a[i]+sum[i-1];
for(int i=2;i<=n;i++){
if((a[i]+1>>1)>sum[i-1]) ans-=(a[i]+1>>1)-sum[i-1]-1;
}
ans+=sum[n]-(a[1]+1>>1)+1;
printf("%lld\n",ans);
return 0;
}
给定一棵先序遍历序是编号序的数,给定M个条件要求u的中序遍历小于v,求方案数n<=400,m<=10e3
树的遍历
先序遍历的性质是一个子数的先序遍历序号是连续的,且第一个为根,考虑DP,枚举两棵树的先序遍历分界线,也就是斯特林数。那么思考一下带条件的斯特林数。
中序遍历的性质是一个左子树的所有小于根小于右子树的所有,因为左右子树和根都是一段连续的区间,两个区间的关系可以使用二维前缀和标记。于是记忆化搜索,枚举分界点,注意,左子树或右子树为空的情况需要考虑进去。
#include
#define maxn 405
#define mod 1000000007
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
LL read(){
LL res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=c^48;
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
int T,n,m;
LL f[maxn][maxn],sum[maxn][maxn];
bool A(int a,int c,int b,int d){
if(a>c || b>d) return 0;
return sum[c][d]+sum[a-1][b-1]-sum[a-1][d]-sum[c][b-1]>0;
}
LL DFS(int x,int y){
if(~f[x][y]) return f[x][y];
LL res=0;
if(x>=y) return 1;
for(int i=x;i<=y;i++){
if(!A(i+1,y,x+1,i)&&!A(x,x,x+1,i)&&!A(i+1,y,x,x)) res=(res+DFS(x+1,i)*DFS(i+1,y)%mod)%mod;
}
return f[x][y]=res;
}
int main(){
T=read();
while(T--){
n=read(); m=read();
memset(f,-1,sizeof f);
memset(sum,0,sizeof sum);
for(int i=1;i<=m;i++){
sum[read()][read()]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
}
}
printf("%lld\n",DFS(1,n));
}
return 0;
}
给定 f [ 1 ] [ i ] f[1][i] f[1][i]和 f [ i ] [ 1 ] f[i][1] f[i][1], f [ n ] [ m ] = a ∗ f [ n − 1 ] [ m ] + b ∗ f [ n ] [ m − 1 ] f[n][m]=a*f[n-1][m]+b*f[n][m-1] f[n][m]=a∗f[n−1][m]+b∗f[n][m−1]
求 f [ n ] [ n ] f[n][n] f[n][n]
考虑每一个 f [ i ] [ 1 ] f[i][1] f[i][1]和 f [ 1 ] [ i ] f[1][i] f[1][i]会被 f [ n ] [ n ] f[n][n] f[n][n]取到几次,因为 f [ n ] [ n ] f[n][n] f[n][n]会向左下角沿一条路走到 f [ i ] [ j ] f[i][j] f[i][j],系数就是路线的方案数。每一个位置会向下和向左走的步数是确定的,所以 a a a和 b b b的指数是确定的,于是就可以跑了,具体的见代码吧。
#include
#define maxn 200005
#define mod 1000000007
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
LL read(){
LL res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=c^48;
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
LL Pow(LL x,LL k){
LL res=1;
while(k){
if(k&1) res=(res*x)%mod;
k>>=1;
x=(x*x)%mod;
}
return res;
}
LL n,a,b,ans,l[maxn],t[maxn],jc[maxn];
LL C(LL b,LL a){
return jc[b]*Pow(jc[a],mod-2)%mod*Pow(jc[b-a],mod-2)%mod;
}
int main(){
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
jc[0]=1;
for(int i=1;i
求包含1的最小环,不能走一下就回来。
考虑朴素的方法,把1拆成出点和入点,出点到入点的最短路就是答案了。然而这样不能避免走一下就回来的问题,考虑枚举,那么我们有两种方案。
一种是枚举中间的某条边,从1开始分别沿正边和反边跑,每次统计答案的时候看两条dis是不是从一个点出来的,如果不是就记ans,这样的效率是两个Dijstra
另一种是枚举开头的两个点,但是这样枚举的复杂度是 n 2 n^2 n2,那么我们可以把点平分成两部分 S , T S,T S,T,这样是对于 u ∈ S , v ∈ T u\in S, v\in T u∈S,v∈T的 ( u , v ) (u,v) (u,v)对我们一边Dijstra统计完了他们的答案,考虑怎样分割使得所有不等的 ( u , v ) (u,v) (u,v)对都被包括,二进制拆分,把每一位不同的拆成两部分这样就可以用 l o g n log\ n log n的效率完成枚举了总效率 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
#include
#define maxn 100005
#define MAXN 200005
#define LL long long
#define INF 1e9
using namespace std;
LL read(){
LL res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=c^48;
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
LL dis[maxn];
struct NODE{
int x,y;
bool operator < (const NODE &rhs)const{
return y>rhs.y;
}
};
struct EDGE{
int u,v,w,nxt;
}e[MAXN];
int cnt=1,head[maxn];
void add(int u,int v,int w){
e[++cnt]=(EDGE){u,v,w,head[u]};
head[u]=cnt;
}
priority_queue Q;
void Dijstra(int s){
memset(dis,0x3f,sizeof dis);
Q.push((NODE){s,0}); dis[s]=0;
while(!Q.empty()){
NODE u=Q.top(); Q.pop();
if(u.y!=dis[u.x]) continue;
for(int i=head[u.x];~i;i=e[i].nxt){
int v=e[i].v,w=e[i].w;
if(dis[u.x]+w>s)&1) add(v[i],n+1,w2[i]);
else add(1,v[i],w1[i]);
}
else if(v[i]==1){
if((u[i]>>s)&1) add(u[i],n+1,w1[i]);
else add(1,u[i],w2[i]);
}
else {add(u[i],v[i],w1[i]); add(v[i],u[i],w2[i]);}
}
Dijstra(1);
ans=min(ans,dis[n+1]);
cnt=0; memset(head,-1,sizeof head);
for(int i=1;i<=m;i++){
if(u[i]==1){
if(!((v[i]>>s)&1)) add(v[i],n+1,w2[i]);
else add(1,v[i],w1[i]);
}
else if(v[i]==1){
if(!((u[i]>>s)&1)) add(u[i],n+1,w1[i]);
else add(1,u[i],w2[i]);
}
else {add(u[i],v[i],w1[i]); add(v[i],u[i],w2[i]);}
}
Dijstra(1);
ans=min(ans,dis[n+1]);
}
printf("%lld\n",ans);
return 0;
}
给定一张边权有正有负的有向图,求经过点数最小的正环
第一,正环非常不好搞,一般这种不好搞的东西考虑二分答案。
第二,跟点数有关的题目很难提前限制或者直接做,所以我们限定点数二分答案。
两种思路都指向二分答案。
于是倍增Floyd,二分即可。每一个值需要 l o g n logn logn求出,所以是 l o g 2 n log^2n log2n
但是更快的方法是用类似倍增LCA的方法做一遍 l o g n logn logn
因为我弱,所以我用的是 O ( n 3 l o g 2 n ) O(n^3log^2n) O(n3log2n)
#include
#define maxn 305
using namespace std;
int n,m,e[10][maxn][maxn],ans[2][maxn][maxn];
inline int MX(int a,int b){
if(a>b) return a;
else return b;
}
bool check(int p){
int pos=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ans[0][i][j]=(i==j?0:-1e9);
}
}
for(int s=0;(1<ans[pos][i][j]){
ans[pos][i][j]=ans[pos^1][i][k]+e[s][k][j];
}
}
}
}
}
}
for(int i=1;i<=n;i++){
if(ans[pos][i][i]>0) return 1;
}
return 0;
}
int main(){
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
for(int s=0;(1<e[s][i][j]){
e[s][i][j]=e[s-1][i][k]+e[s-1][k][j];
}
}
}
}
}
int l=2,r=n+1;
while(l>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l%(n+1));
}