2019牛客暑期多校訓練營(第一場)

Solutions


A:Equivalent Prefixes

題意:node

若$\text{RMQ(u,l,r)=RMQ(v,l,r)}$ for all $1{\leq}l{\leq}r{\leq}m$,則爲$equivalent$ios

其中$\text{RMQ(w,l,r)}$的定義是${[l,r]}$區間內最小數的下標c++

給兩個數組,每組元素各不相同,求最大的$p{\leq}n\ where\ \{a_1,a_2,{\ldots}a_p\} are\ equivalent$ 數組

單調棧思路:dom

處理出每一位左邊第一個比它小的下標,而後相同的話可取,不一樣不可取。ide

假如當前處理$a_i$,(也就是$\{a_1,a_2,{\ldots}a_{i-1}\}$知足條件),假設比它第一個小的是$last[i]$,那麼$last[i]$右邊的$RMQ$必定是$a_i$,這樣的話,$last[i]$左邊的$RMQ$必定與新數無關,由於僅僅$last[i]$處就已經比$a_i$小了。ui

能夠利用單調棧求每一位左邊第一個比它小的下標idea

 1 #include<bits/stdc++.h>
 2 using namespace std;  3 const int maxn=1e5+10;  4 
 5 int n,a[maxn],b[maxn],ida[maxn],idb[maxn];  6 
 7 stack<pair<int,int> > s;  8 
 9 int main() { 10     while(~scanf("%d",&n)) { 11         s.push(make_pair(-1,-1)); 12         for(int i=0;i<n;i++) { 13             scanf("%d",&a[i]); 14             while(!s.empty()&&s.top().first>a[i]) s.pop(); 15             ida[i]=s.top().second; 16  s.push(make_pair(a[i],i)); 17  } 18         while(!s.empty()) s.pop(); 19         s.push(make_pair(-1,-1)); 20         for(int i=0;i<n;i++) { 21             scanf("%d",&b[i]); 22             while(!s.empty()&&s.top().first>b[i]) s.pop(); 23             idb[i]=s.top().second; 24  s.push(make_pair(b[i],i)); 25  } 26         int cur=0; 27         while(cur<n) { 28             if(ida[cur]==idb[cur]) cur++; 29             else break; 30  } 31         printf("%d\n",cur); 32  } 33     return 0; 34 }

二分+分治+ST思路:spa

 經過$ST$表預處理出$RMQ$,$p$具備單調性,能夠二分判斷是否知足條件,而後詢問$1{\sim}mid$最小值,再分治.net

 1 #include<bits/stdc++.h>
 2 using namespace std;  3 const int maxn=100010;  4 
 5 int dpa[maxn][20],dpb[maxn][20],a[maxn],b[maxn];  6 int mm[maxn],n,ida[maxn],idb[maxn];  7 
 8 void init() {  9     mm[0]=-1; 10     for(int i=1;i<=n;i++) { 11         mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1]; 12         dpa[i][0]=a[i]; 13         dpb[i][0]=b[i]; 14  } 15     for(int j=1;j<=mm[n];j++) { 16         for(int i=1;i+(1<<j)-1<=n;i++) { 17             dpa[i][j]=min(dpa[i][j-1],dpa[i+(1<<(j-1))][j-1]); 18             dpb[i][j]=min(dpb[i][j-1],dpb[i+(1<<(j-1))][j-1]); 19  } 20  } 21 } 22 
23 int rmqa(int x,int y) { 24     int k=mm[y-x+1]; 25     return ida[min(dpa[x][k],dpa[y-(1<<k)+1][k])]; 26 } 27 
28 int rmqb(int x,int y) { 29     int k=mm[y-x+1]; 30     return idb[min(dpb[x][k],dpb[y-(1<<k)+1][k])]; 31 } 32 
33 bool dfs(int l,int r) { 34     if(l>=r) return true; 35     int x=rmqa(l,r); 36     int y=rmqb(l,r); 37     if(x!=y) return false; 38     else return dfs(1,x-1)&&dfs(y+1,r); 39 } 40 
41 bool check(int mid) { 42     return dfs(1,mid); 43 } 44 
45 int main() { 46     while(~scanf("%d",&n)) { 47         for(int i=1;i<=n;i++) { 48             scanf("%d",&a[i]); 49             ida[a[i]]=i; 50  } 51         for(int i=1;i<=n;i++) { 52             scanf("%d",&b[i]); 53             idb[b[i]]=i; 54  } 55  init(); 56         int l=1,r=n,ans=1; 57         while(l<=r) { 58             int mid=(l+r)>>1; 59             if(check(mid)) { 60                 ans=mid; 61                 l=mid+1; 62             } else { 63                 r=mid-1; 64  } 65  } 66         printf("%d\n",ans); 67  } 68 }

B: Integration

題意:

給出$n$個數,$a_1,a_2,\ldots,a_n$,求$\frac1\pi\int_0^{\infty}\frac1{\prod_{i=0}^n(a_i^2+x^2)}dx$

由於分母是連乘積,因此能夠列項拆分。推幾項能夠得出規律

具體參考這裏

 1 #include<bits/stdc++.h>
 2 using namespace std;  3 const int maxn=1010;  4 typedef long long ll;  5 const int mod=1e9+7;  6 
 7 ll a[maxn];  8 int n;  9 
10 ll q_power(ll a,ll b) { 11     ll ans=1,tmp=a%mod; 12     while(b) { 13         if(b&1) ans=ans*tmp%mod; 14         tmp=tmp*tmp%mod; 15         b>>=1; 16  } 17     return ans; 18 } 19 
20 int main() { 21     while(~scanf("%d",&n)) { 22         for(int i=1;i<=n;i++) scanf("%lld",&a[i]); 23         ll ans=0; 24         for(int i=1;i<=n;i++) { 25             ll tmp=1; 26             for(int j=1;j<=n;j++) { 27                 if(i==j) continue; 28                 tmp=tmp*(((a[j]*a[j]%mod)-(a[i]*a[i]%mod)+mod)%mod)%mod; 29  } 30             tmp=tmp*2*a[i]%mod; 31             ans=(ans+q_power(tmp,mod-2))%mod; 32  } 33         printf("%lld\n",ans); 34  } 35 }

C:Euclidean Distance

題意:

給出$A$點的$n$維座標$A=(a_1/m,a_2/m,\ldots,a_n/m)$,請找一點$P=(p_1,p_2,\ldots,p_n)$,知足一下條件

  • $p_1,p_2,\ldots,p_n\ \in{\mathbb{R}}$
  • $p_1,p_2,\ldots,p_n{\geq}0$
  • $p_1+p_2+\ldots+p_n=1$

使得$\sum_{i=1}^n(a_i/m-p_i)^2$最小。

思路:

參考這裏

首先,咱們能夠將$p$的座標放大m倍,那麼答案能夠轉化爲:

$\sum_{i=1}^n(a_i/m-p_i)^2\ =\ \sum_{i=1}^n[m(a_i/m-p_i)]^2\ \Rightarrow\ \frac1{m^2}\sum_{i=1}^n(a_i-mpi)^2$

考慮$a_i$爲每一個矩形的高,減去的$m$至關於削減矩形的高度,首先將$a_1,a_2,\ldots,a_n$降序排列。證:

削減高度較大的收益會比削減高度較小的收益更大,(好比負數,減去數反而變大了)。

如圖中的綠線,把矩形一點一點往下推,而後下一步一塊兒推,道理同上。

若不能進一步往下推,就把總體減一點點。能夠預處理出前綴和。而後判斷夠不夠把$a_i$前面的所有推平。

具體細節能夠見上面大佬的博客。

 1 #include<bits/stdc++.h>
 2 using namespace std;  3 #define ll long long
 4 int n,m,a[10010],sum[10010];  5 
 6 bool cmp(int a,int b) {  7     return a>b;  8 }  9 
10 int main() { 11     while(~scanf("%d%d",&n,&m)) { 12         for(int i=1;i<=n;i++) scanf("%d",&a[i]); 13         sort(a+1,a+n+1,cmp); 14         sum[0]=-m; 15         for(int i=1;i<=n;i++) { 16             sum[i]=sum[i-1]+a[i]; 17  } 18  ll frac1,frac2; 19         int now=n; 20         for(int i=1;i<n;i++) { 21             if(sum[i]>a[i+1]*i) { 22                 now=i; 23                 break; 24  } 25  } 26         frac1=1ll*sum[now]*sum[now]*now; 27         frac2=now*now; 28         for(int i=now+1;i<=n;i++) { 29             frac1+=1ll*a[i]*a[i]*frac2; 30  } 31         frac2=frac2*1ll*m*m; 32         ll tmp=__gcd(frac1,frac2); 33         if(frac2/tmp==1) printf("%lld\n",frac1/tmp); 34         else if(!(frac1/tmp)) printf("0\n"); 35         else printf("%lld/%lld\n",frac1/tmp,frac2/tmp); 36  } 37 }

E:ABBA

題意:

給出$2(n+m)$個$A,B$構成序列,求能夠劃分爲$n$個$AB$,$m$個$BA$的序列的個數。

思路:

首先貪心的想,前$n$個$A$是$AB$的$A$,前$m$個$B$是$BA$的$B$。

而後$dp[i][j]$表示放了$i$個$A$,$j$個$B$的方案數。

若$i{\leq}n$,$A$能夠直接放

若$j{\leq}m$,$B$能夠直接放

若$i-n{\leq}j$,即如今用於組合$BA$的$A$的數量小於$B$的數量,$A$能夠放

若$j-m{\leq}i$,即如今用於組合$AB$的$B$的數量小於$A$的數量,$B$能夠放

 1 #include<bits/stdc++.h>
 2 using namespace std;  3 const int mod=1e9+7;  4 
 5 int dp[2010][2010];  6 int n,m;  7 
 8 int main() {  9     while(~scanf("%d%d",&n,&m)) { 10         for(int i=0;i<=n+m;i++) { 11             for(int j=0;j<=n+m;j++) 12                 dp[i][j]=0; 13  } 14         dp[0][0]=1; 15         for(int i=0;i<=n+m;i++) { 16             for(int j=0;j<=n+m;j++) { 17                 if(i-n<j) dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod; 18                 if(j-m<i) dp[i][j+1]=(dp[i][j+1]+dp[i][j])%mod; 19  } 20  } 21         printf("%d\n",dp[n+m][n+m]); 22  } 23 }

F:Random Point in Triangle

題意:

給出三角形三點座標$A,B,C$,求

$$E=max\{S_{PAB},S_{PBC},S_{PCA}\}$$

輸出$36{\times}E$

思路:

暫時只知道$ans=22{\times}S_{ABC}$,證實不會,記一下叉積求面積。$S_{ABC}=\frac12\vec{AB}{\times}\vec{AC}$

 1 #include<iostream>
 2 #include<cstdio>
 3 #define lowbit(x) (x&(-x))
 4 #include<cstring>
 5 using namespace std;  6 typedef long long ll;  7 
 8 int main() {  9  ll x1,y1,x2,y2,x3,y3; 10     while(~scanf("%lld%lld%lld%lld%lld%lld",&x1,&y1,&x2,&y2,&x3,&y3)) { 11         ll ans=abs(x2*y3-x2*y1-x1*y3-y2*x3+x1*y2+y1*x3); 12         printf("%lld\n",22/2*ans); 13  } 14 }

I:Points Division

題意:

給出$n$個點,每一個點有兩個權值$a_i,b_i$,而後把這些點劃分爲兩個集合,若點$i$在$A$中,則貢獻爲$a_i$,若若點$i$在$B$中,則貢獻爲$b_i$,

要求集合$A$在集合$B$的左上方。求劃分後的最大權值和。

思路:

參考這裏

顯然能夠用一條遞增的折線劃分爲兩個區域。而後就開始玄學了。

$dp[i]$表示第$i$個點在折線上時的最大權值和。

當加入$i$時,若是$y_i$大於以前的$y_j$,則$i$的貢獻爲$a_i$,若是$y_i$小於以前的$y_j$,則$i$的貢獻爲$b_i$

那麼$dp[j]=
\begin{cases}
dp[j]+b_i & j<i,y_j>y_i\\
dp[j]+a_i & j<i,y_j<y_i
\end{cases}$

$dp[i]=b_i+max_{1{\leq}<i,y_j<y_i}dp[j]$

式子能夠用線段樹維護區間最值(可是我比較迷,看不太懂)

還須要多加一個點。由於dp初始化爲0。

(僅複習下線段樹區間修改時的最大值,注意可能L>R,因此判一下)

 1 #include<bits/stdc++.h>
 2 using namespace std;  3 const int maxn=1e5+10;  4 typedef long long ll;  5 const int inf=0x3f3f3f3f;  6 
 7 struct node{  8  ll x,y,a,b;  9 }p[maxn];  10 ll t[maxn];  11 int tot,cnt,n;  12 
 13 bool cmp(node a,node b) {  14     if(a.x==b.x) return a.y>b.y;  15     else return a.x<b.x;  16 }  17 
 18 ll maxx[maxn<<2],lazy[maxn<<2];  19 
 20 void pushup(int rt) {  21     maxx[rt]=max(maxx[rt<<1],maxx[rt<<1|1]);  22 }  23 
 24 void build(int l,int r,int rt) {  25     lazy[rt]=maxx[rt]=0;  26     if(l==r) return ;  27     int mid=(l+r)>>1;  28     build(l,mid,rt<<1);  29     build(mid+1,r,rt<<1|1);  30  pushup(rt);  31 }  32 
 33 void pushdown(int rt) {  34     if(lazy[rt]) {  35         lazy[rt<<1]+=lazy[rt];  36         lazy[rt<<1|1]+=lazy[rt];  37         maxx[rt<<1]+=lazy[rt];  38         maxx[rt<<1|1]+=lazy[rt];  39         lazy[rt]=0;  40  }  41 }  42 
 43 void updata1(int pos,ll val,int l,int r,int rt) {  44     if(l==r) {  45         maxx[rt]=max(maxx[rt],val);  46         return ;  47  }  48     int mid=(l+r)>>1;  49  pushdown(rt);  50     if(pos<=mid) updata1(pos,val,l,mid,rt<<1);  51     else updata1(pos,val,mid+1,r,rt<<1|1);  52  pushup(rt);  53 }  54 
 55 void updata2(int L,int R,int val,int l,int r,int rt) {  56     if(L<=l&&R>=r) {  57         maxx[rt]+=val;  58         lazy[rt]+=val;  59         return ;  60  }  61     int mid=(l+r)>>1;  62  pushdown(rt);  63     if(L<=mid) updata2(L,R,val,l,mid,rt<<1);  64     if(R>mid) updata2(L,R,val,mid+1,r,rt<<1|1);  65  pushup(rt);  66 }  67 
 68 ll query(int L,int R,int l,int r,int rt) {  69     if(L<=l&&R>=r) {  70         return maxx[rt];  71  }  72     ll ans=0;  73     int mid=(l+r)>>1;  74  pushdown(rt);  75     if(L<=mid) ans=max(ans,query(L,R,l,mid,rt<<1));  76     if(R>mid) ans=max(ans,query(L,R,mid+1,r,rt<<1|1));  77     return ans;  78 }  79 
 80 int main() {  81     while(~scanf("%d",&n)) {  82         cnt=0;  83         for(int i=1;i<=n;i++) {  84             scanf("%lld%lld%lld%lld",&p[i].x,&p[i].y,&p[i].a,&p[i].b);  85             t[++cnt]=p[i].y;  86  }  87         sort(p+1,p+n+1,cmp);  88         sort(t+1,t+cnt+1);  89         tot=unique(t+1,t+cnt+1)-t-1;  90         for(int i=1;i<=n;i++) {  91             p[i].y=lower_bound(t+1,t+tot+1,p[i].y)-t+1;  92  }  93         tot++;  94         build(1,tot,1);  95         for(int i=1;i<=n;i++) {  96             ll ans=query(1,p[i].y,1,tot,1);  97             updata1(p[i].y,ans+p[i].b,1,tot,1);  98             updata2(1,p[i].y-1,p[i].a,1,tot,1);  99             if(p[i].y+1<=tot) updata2(p[i].y+1,tot,p[i].b,1,tot,1); 100  } 101         printf("%lld\n",maxx[1]); 102  } 103 }

J:Fraction Comparision

題意:

判斷$\frac{x}a$與$\frac{y}b$的大小關係

  • $0{\leq}x,y{\leq}10^{18}$
  • $1{\leq}a,b{\leq}10^9$

思路:

  • 不能用浮點數判斷(精度不夠?)
  • 把$\frac{x}a$寫成${\lfloor}{\frac{x}a}{\rfloor}+{x\%a}$ 先比較整數部分,再交叉相乘比較分數
 1 #include<bits/stdc++.h>
 2 using namespace std;  3 typedef long long ll;  4 
 5 void out(ll x,ll y) {  6     if(x==y)  puts("=");  7     else if(x>y) puts(">");  8     else puts("<");  9 } 10 
11 
12 int main() { 13  ll x,a,y,b; 14     while(~scanf("%lld%lld%lld%lld",&x,&a,&y,&b)) { 15         ll A=x/a; 16         ll B=y/b; 17         x%=a; 18         y%=b; 19         if(A!=B) out(A,B); 20         else out(x*b,y*a); 21  } 22     return 0; 23 }
相關文章
相關標籤/搜索