給兩個序列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; }