给两个序列a和b,找出最大一个位置p,使得两个序列1-p的子序列中,任意区间的最小值位置相同。node
最小值的位置考虑用单调栈预处理出每一个数做为最小值的最左和最右的位置,而后从1开始枚举,对于某个位置i,若是\(a_i\)和\(b_i\)做为最小值覆盖区间的左端点不一样,那么确定不行,直接break,由于若是选择这个做为p,显然存在一个区间最小值位置不一样。python
若是左端点相同,考虑右端点,显然咱们的p最大能够取到两个右端点的小值,所以咱们的i也最多就枚举到p,并且p是不断往小的更新。c++
#include <bits/stdc++.h> using namespace std; const int N=1e5+50; int n,a[N],b[N]; stack<int> ss; int ale[N],ari[N],ble[N],bri[N]; int main(void){ //freopen("in.txt","r",stdin); while(~scanf("%d",&n)){ for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int i=1;i<=n;i++){ scanf("%d",&b[i]); } while(!ss.empty()){ ss.pop(); } for(int i=1;i<=n;i++){ while(ss.size()>0 && a[i]<=a[ss.top()]){ ss.pop(); } if(ss.size()>0){ ale[i]=ss.top()+1; }else{ ale[i]=1; } ss.push(i); } while(!ss.empty()){ ss.pop(); } for(int i=n;i>=1;i--){ while(ss.size()>0 && a[i]<=a[ss.top()]){ ss.pop(); } if(ss.size()>0){ ari[i]=ss.top()-1; }else{ ari[i]=n; } ss.push(i); } while(!ss.empty()){ ss.pop(); } for(int i=1;i<=n;i++){ while(ss.size()>0 && b[i]<=b[ss.top()]){ ss.pop(); } if(ss.size()>0){ ble[i]=ss.top()+1; }else{ ble[i]=1; } ss.push(i); } while(!ss.empty()){ ss.pop(); } for(int i=n;i>=1;i--){ while(ss.size()>0 && b[i]<=b[ss.top()]){ ss.pop(); } if(ss.size()>0){ bri[i]=ss.top()-1; }else{ bri[i]=n; } ss.push(i); } int p=0,R=n; bool flag=true; for(int i=1;i<=R;i++){ if(ale[i]!=ble[i]){ flag=false; break; }else{ if(ari[i]!=bri[i]) { p=R=min(ari[i],bri[i]); flag=false; } } } //特判全部区间左右端点都相等 if(flag){ p=n; } printf("%d\n",p); } return 0; }
已知\(\int_{0}^{\infty}\frac{1}{1+x^2}=\frac{\pi}{2}\),如今给一个序列\(a_1,a_1...a_n\),求\(\frac{1}{\pi}\int_0^{\infty}\frac{1}{\prod_{i=1}^n(a_i^2+x^2)}dx\)dom
因为原式\(\frac{1}{\prod_{i=1}^n(a_i^2+x^2)}\)分母是乘积的形式,考虑经过裂项化为相加的形式,并使用待定系数法,也就是\(\frac{C_1}{(a_1^2+x^2)}+\frac{C_2}{(a_2^2+x^2)}+...+\frac{C_n}{(a_n^2+x^2)}=\frac{1}{\prod_{i=1}^n(a_i^2+x^2)}\),如今为了求出\(C_1\),咱们在方程左右两边同乘\(a_1^2+x^2\)并移项,获得$ C_1=\frac{a_1^2+x^2}{\prod_{i=1}^n(a_i^2+x^2)}-(\frac{C_2*a_1^2+x^2}{(a_2^2+x^2)}+...+\frac{C_n*a_1^2+x^2}{(a_n^2+x^2)})\(,那么若是咱们取\)x^2=-a_1^2\(,代入能够获得\)C_1=\frac{1}{\prod_{i=1}^n(a_i^2-a_1^2)(i!=1)}$,同理咱们能够O(n^2)算出这全部系数。ui
求出系数以后,原式即为\(\frac{1}{\pi}\int_0^{\infty}\frac{1}{\prod_{i=1}^n(a_i^2+x^2)}dx=\frac{1}{\pi}\int_0^{\infty}\sum \frac{c_i}{a_i^2+x^2}dx=\frac{1}{\pi}\sum\frac{1}{a_i^2}\int_0^{\infty}\frac{c_i}{1+(\frac{x}{a_i})^2}dx\),换元,由题意可获得结果为\(\frac{1}{\pi}\sum\frac{c_i}{2a_i}\pi=\sum\frac{c_i}{2a_i}\)。spa
#include <bits/stdc++.h> using namespace std; const int N=1e3+50; typedef long long ll; const ll mod=1e9+7; int n; ll a[N],c[N]; ll Pow(ll a,ll n){ ll ans=1ll; while(n){ if(n%2){ ans=ans*a%mod; } a=a*a%mod; n/=2; } return ans; } ll inv(ll x){ return Pow(x,mod-2); } int main(void){ while(~scanf("%d",&n)){ for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); } for(int i=1;i<=n;i++){ c[i]=1ll; for(int j=1;j<=n;j++){ if(i==j){ continue; } c[i]=(c[i]*(a[j]*a[j]%mod-a[i]*a[i]%mod+mod))%mod; } c[i]=inv(c[i]); } ll ans=0; for(int i=1;i<=n;i++){ ans=(ans+inv(2)*inv(a[i])%mod*c[i])%mod; } printf("%lld\n",ans); } return 0; }
给定n个AB和m个BA,问这n+m的子序列能组成多少种方案的字符串。code
定义状态\(dp[i][j]\)表示i个A和j个B组成的前缀方案数,显然若是转移的方案都合法,那么是\(dp[i][j]=dp[i-1][j]+dp[i][j-1]\),可是并非每次转移都是合法的。字符串
例如对A来讲,但\(i<=n\)时,再放一个A显然是合法的,后面确定能够放B,当\(i>n\)时,这时候再放A确定是前面有B才行了,也就是\(j>(i-n)\)。博客
放B也是同理。it
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e3+50; const ll mod=1e9+7; int n,m; //dp[i][j]表示前i+j个字符中放了i个A和j个B的方案数 ll dp[N][N]; int main(void){ // freopen("in.txt","r",stdin); while(~scanf("%d%d",&n,&m)){ for(int i=0;i<=n+m;i++){ for(int j=0;j<=n+m;j++){ dp[i][j]=0; } } //初始化,只含A或者只含B只有一种方案 for(int i=0;i<=n;i++){ dp[i][0]=1; } for(int i=0;i<=m;i++){ dp[0][i]=1; } //出现一个A就当作是AB的A,出现一个B就当作是BA的B //由于假设这个A是BA的A,那么只要这个A不超过总个数,就必定能在后面找到一个A来代替 //所以状态转移的时候,i和j也就是A和B的个数只要保证不超过总个数便可 for(int i=1;i<=n+m;i++){ for(int j=1;j<=n+m;j++){ //在前面合法的方案状态中加入一个A //i-j的数量就是至少的AB数量(假设前面都是BABA..),要<=n if(i<=n || min(j,m)>=(i-n)){ dp[i][j]=(dp[i][j]+dp[i-1][j])%mod; } //在前面合法的方案状态中加入一个B if(j<=m || min(i,n)>=(j-m)){ dp[i][j]=(dp[i][j]+dp[i][j-1])%mod; } } } printf("%lld\n",dp[n+m][n+m]%mod); } return 0; }
给一个三角形,在三角形内任意取一个点,分红三个三角形,问最大值的指望。
显然只会跟三角形面积有关,由于两个不一样形状的三角形确定能找到一个对应的点使得二者划分出的三角形面积相同,因此随机撒点(注意用面积大一点的三角形)获得答案是22倍三角形面积。
#include <bits/stdc++.h> using namespace std; typedef long long ll; //次数 int n=100000; double randf(){ return (double)(rand()/(double)RAND_MAX); } ll area(ll x1,ll y1,ll x2,ll y2,ll x3,ll y3){ return abs(((x1*y2-x2*y1)+(x2*y3-x3*y2)+(x3*y1-x1*y3))*11ll); } ll ax1,ay1,ax2,ay2,ax3,ay3; int main(void){ while(~scanf("%lld%lld%lld%lld%lld%lld",&ax1,&ay1,&ax2,&ay2,&ax3,&ay3)){ ll ans=area(ax1,ay1,ax2,ay2,ax3,ay3); printf("%lld\n",ans); } return 0; }
给n个数,求全部异或为0的子集大小之和。
异或为0的子集想到线性基,求大小之和通常反过来考虑每一个数的贡献,也就是这个数在多少个子集中能够异或获得0。
先将\(n\)个数放入线性基获得\(r\)个基底,对于不在线性基中的\(n-r\)个数,他们的任意组合均可以用线性基中的一个基底组合来线性表示,所以对于这\(n - r\)不在线性基中的数,贡献为\((n - r)*(2^{n-r-1})\),即固定一个数,其余任选。
根据线性基的性质,若是有两个不一样的线性基,那么他们的大小是相同的,都是\(r\),所以,对于在线性基中的\(r\)个数,咱们依次枚举,而后将剩下的\(n-1\)个数加入到另外一个线性基中,这时候枚举的这个数\(x\)就不在线性基内了,至关于第一种状况,咱们只要判断\(x\)是否能由线性基中的基底表示,若能,则贡献为\(2^{n-r-1}\)。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+50; const ll mod=1e9+7; struct LB{ ll a[65],p[65]; int cnt,num; void init(){ memset(a,0,sizeof(a)); memset(p,0,sizeof(p)); cnt=0; num=0; } //用!ins判断是否能用基底表示 bool ins(ll x){ for(int i=63;i>=0;i--){ if((x>>i)&1){ //以前这一位没出现过1 if(!a[i]){ a[i]=x; num++; break; } //以前出现过,把x异或掉 x^=a[i]; } } //所有位被异或掉,则插入失败 return x>0; } bool exist(ll x){ //同插入 for(int i=60;i>=0;i--){ if((x>>i)&1){ x^=a[i]; if(!x){ return true; } } } return false; } }A,B,C; int n; ll a[N]; bool vis[N]; vector<ll> Ab; ll Pow(ll a,ll n){ ll ans=1ll; while(n){ if(n%2){ ans=ans*a%mod; } a=a*a%mod; n/=2; } return ans; } int main(void){ //freopen("in.txt","r",stdin); while(~scanf("%d",&n)){ for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); } Ab.clear(); for(int i=1;i<=n;i++){ vis[i]=false; } //先求第一个线性基A A.init(); B.init(); for(int i=1;i<=n;i++){ vis[i]=A.ins(a[i]); if(vis[i]){ Ab.push_back(a[i]); }else{ B.ins(a[i]); } } int r=Ab.size(); if(r==n){ printf("0\n"); continue; } //计算不在线性基外n-r个数的贡献 (n-r)*2^(n-r-1) ll ans=1ll*(n-r)*Pow(2ll,n-r-1)%mod; //枚举A线性基中的r个数,将其余n-1个数加入另外一个线性基中 //由线性基性质,若是枚举的x能由其余n-1个数的线性基表示,那么这个线性基的大小也是r //因此x的贡献也是2^(n-r-1) //先处理出n-r个数的线性基,每次枚举再加入r-1个原线性基中的数 C.init(); for(int i=0;i<r;i++){ C=B; for(int j=0;j<r;j++){ if(i==j){ continue; } C.ins(Ab[j]); } if(C.exist(Ab[i])){ ans=(ans+(Pow(2ll,n-r-1)%mod))%mod; } } printf("%lld\n",ans%mod); } return 0; }
二维平面上有n个点,每一个点有一个a和b值,将n个点分为AB两部分,使得不存在一个A的点在B的点的右下方,求\(max(\sum_{i\sub A}ai+\sum_{i\sub B}bi)\)
#include <bits/stdc++.h> using namespace std; #define ls i<<1 #define rs i<<1|1 #define mid (l+r)/2 typedef long long ll; const int N=1e5+50; int n,m; vector<int> yy; struct node{ int x,y; ll a,b; bool operator <(const node& rhs)const{ //x从小到大 y从大到小 具体缘由见博客 if(x==rhs.x){ return y>rhs.y; } return x<rhs.x; } }p[N]; ll mx[N*4],lz[N*4]; void pushup(int i){ mx[i]=max(mx[ls],mx[rs]); } void pushdown(int i){ if(lz[i]){ lz[ls]+=lz[i]; lz[rs]+=lz[i]; mx[ls]+=lz[i]; mx[rs]+=lz[i]; lz[i]=0; } } void build(int i,int l,int r){ lz[i]=0; if(l==r){ mx[i]=0; return; } build(ls,l,mid); build(rs,mid+1,r); pushup(i); } void update(int i,int l,int r,int p,ll v){ if(l==r && p==l){ mx[i]=max(mx[i],v); return; } //由于存在区间更新,单点更新也要pushdown pushdown(i); if(p<=mid){ update(ls,l,mid,p,v); }else{ update(rs,mid+1,r,p,v); } pushup(i); } void update(int i,int l,int r,int ql,int qr,ll v){ if(ql<=l && qr>=r){ lz[i]+=v; mx[i]+=v; return; } pushdown(i); if(ql<=mid){ update(ls,l,mid,ql,qr,v); } if(qr>mid){ update(rs,mid+1,r,ql,qr,v); } pushup(i); } ll query(int i,int l,int r,int ql,int qr){ if(ql<=l && qr>=r){ return mx[i]; } ll ans=0; pushdown(i); if(ql<=mid){ ans=max(ans,query(ls,l,mid,ql,qr)); } if(qr>mid){ ans=max(ans,query(rs,mid+1,r,ql,qr)); } return ans; } int main(void){ // freopen("in.txt","r",stdin); while(~scanf("%d",&n)){ yy.clear(); for(int i=1;i<=n;i++){ scanf("%d%d%lld%lld",&p[i].x,&p[i].y,&p[i].a,&p[i].b); yy.push_back(p[i].y); } //y离散化 sort(yy.begin(),yy.end()); yy.erase(unique(yy.begin(),yy.end()),yy.end()); m=yy.size(); for(int i=1;i<=n;i++){ p[i].y=lower_bound(yy.begin(),yy.end(),p[i].y)-yy.begin()+2; } //增长一个虚拟点,计算第一个点的a贡献 m++; sort(p+1,p+1+n); build(1,1,m); for(int i=1;i<=n;i++){ ll tmp=query(1,1,m,1,p[i].y); update(1,1,m,p[i].y,tmp+p[i].b); update(1,1,m,1,p[i].y-1,p[i].a); if(p[i].y+1<=m){ update(1,1,m,p[i].y+1,m,p[i].b); } } printf("%lld\n",mx[1]); } return 0; }
判断两个分数大小,分子范围1e18
Java或者python大数或者__int128能够直接水过。
不用大数的话能够先比较整数部分,整数部分相同再取模化成真分数,而后交叉乘比较。
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll x,y,a,b; int main(void){ while(~scanf("%lld%lld%lld%lld",&x,&a,&y,&b)){ ll xa=x/a; ll yb=y/b; if(xa>yb){ printf(">\n"); }else if(xa<yb){ printf("<\n"); }else{ ll xm=(x%a)*b; ll ym=(y%b)*a; if(xm>ym){ printf(">\n"); }else if(xm<ym){ printf("<\n"); }else{ printf("=\n"); } } } return 0; }