2019牛客多校训练营(一)

文章目录

  • A.Equivalent Prefixes
    • 题目
    • 题解
    • 代码
  • B. Integration
    • 题目
    • 题解
    • 代码
  • C. Euclidean Distance
    • 题目
    • 题解
    • 代码
  • E. ABBA
    • 题目
    • 题解
    • 代码
  • F. Random Point in Triangle
    • 题目
    • 题解
    • 代码
  • J. Fraction Comparision
    • 题目
    • 题解
    • 代码

链接: https://ac.nowcoder.com/acm/contest/881#question

A.Equivalent Prefixes

题目

若RMQ(u,l,r)==RMQ(v,l,r)对于所有 1 ≤ l ≤ r ≤ n 1\leq l \leq r \leq n 1lrn都成立,那么它们equivalent
RMQ(w,l,r)表示[l,r]区间中最小值的下标。

已知n,m,两个数列a和b。要求一个最大的p( p ≤ n p\leq n pn),使得 { a 1 , a 2 , … , a p } \left\{a_{1}, a_{2}, \dots, a_{p}\right\} {a1,a2,,ap} { b 1 , b 2 , … , b p } \left\{b_{1}, b_{2}, \ldots, b_{p}\right\} {b1,b2,,bp},要求这两段equivalent

题解

以a数列为例,对于每一个i,要求以 a i a_i ai为最小元素所能扩充到的左右区间[l,r],再对 b i b_i bi求相同的区间[l’,r’],如果r==r’,那么i+1,当前结果变为r,否则的话,当前结果就变为min(r,r’)。因为可能对于i来说,最右的区间为r,而对于i+1来说,最右的区间

求以一个值为最小值的区间可以用单调栈来完成。单调栈维护一个递增的序列。

代码

#include
using namespace std;
 
const int maxn = 200005;
 
template <class T>
void read(T &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0'; s=getchar();}
    x *= f;
}
 
int n,a[maxn],b[maxn],l[maxn],r[maxn],p[maxn],q[maxn];
 
void exec() {
    stack<int> s;
    int temp;
    for(int i=1;i<=n;i++) {
        while(!s.empty() && a[s.top()]>a[i]) {
            r[s.top()] = i-1;
            l[i] = l[s.top()];
            s.pop();
        }
        s.push(i);
    }
    if(!s.empty()) temp = s.top();
    while(!s.empty()) {
        r[s.top()] = temp;
        s.pop();
    }
 
    for(int i=1;i<=n;i++) {
        while(!s.empty() && b[s.top()]>b[i]) {
            q[s.top()] = i-1;
            p[i] = p[s.top()];
            s.pop();
        }
        s.push(i);
    }
    if(!s.empty()) temp = s.top();
    while(!s.empty()) {
        q[s.top()] = temp;
        s.pop();
    }
}
 
 
int main() {
    while(~scanf("%d",&n)) {
        int res = 0x7fffffff;
        for(int i=1;i<=n;i++) read(a[i]),l[i]=r[i]=i;
        for(int i=1;i<=n;i++) read(b[i]),p[i]=q[i]=i;
        exec();
        int temp = res;
        for(int i=1;i<=n;i++) {
            if(r[i]!=q[i]) {
                temp = min(temp,min(r[i],q[i]));
            } else res = r[i];
        }
        res = min(res,temp);
        cout << res << endl;
    }
    return 0;
}

B. Integration

题目

已知 ∫ 0 ∞ 1 1 + x 2 d x = π 2 \int_{0}^{\infty} \frac{1}{1+x^{2}} d x=\frac{\pi}{2} 01+x21dx=2π,先在给定n个数。
要求 1 π ∫ 0 ∞ 1 ∏ i = 1 n ( a i 2 + x 2 ) d x \frac{1}{\pi} \int_{0}^{\infty} \frac{1}{\prod_{i=1}^{n}\left(a_{i}^{2}+x^{2}\right)} \mathrm{d} x π10i=1n(ai2+x2)1dx
因为结果是分数形式 p q \frac{p}{q} qp,要求输出一个整数解,即 ( p ⋅ q − 1 )   m o d   ( 1 0 9 + 7 ) \left(p \cdot q^{-1}\right) \bmod \left(10^{9}+7\right) (pq1)mod(109+7)

题解

不难想到,由于要求的式子是乘法形式,应当裂项成若干个式子相加。
c i = 1 ∏ j ≠ i ( a j 2 − a i 2 ) c_{i}=\frac{1}{\prod_{j \neq i}\left(a_{j}^{2}-a_{i}^{2}\right)} ci=j̸=i(aj2ai2)1,
1 ∏ ( a i 2 + x 2 ) = ∑ c i a i 2 + x 2 \frac{1}{\prod\left(a_{i}^{2}+x^{2}\right)}=\sum \frac{c_{i}}{a_{i}^{2}+x^{2}} (ai2+x2)1=ai2+x2ci
∫ 0 ∞ c i a i 2 + x 2 d x = c i 2 a i π \int_{0}^{\infty} \frac{c_{i}}{a_{i}^{2}+x^{2}} d x=\frac{c_{i}}{2 a_{i}} \pi 0ai2+x2cidx=2aiciπ
最终将积分化成求和的形式即可。

c i c_i ci的一步颇为有趣,https://www.cnblogs.com/yanlifneg/p/11211455.html#commentform
该博客中提到的方法有利用留数法裂项,个人认为是一个不错的方法。

代码

#include
using namespace std;
   
const int maxn = 1005;
typedef long long ll;
const ll mo = 1e9+7;
template <class T>
void read(T &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0'; s=getchar();}
    x *= f;
}
  
ll pow(ll a,ll b,ll p) {
    ll res = 1;
    while(b) {
        if(b&1) res = res*a%p;
        a = a*a%p;
        b >>= 1;
    }
    return res;
}
  
ll n;
ll a[maxn],c[maxn];
int main() {
    while(~scanf("%lld",&n)) {
        for(int i=0;i<n;i++) read(a[i]);
        for(int i=0;i<n;i++) {
            c[i] = 1;
            for(int j=0;j<n;j++) {
                if(j!=i) c[i] = c[i]*((a[j]*a[j]%mo-a[i]*a[i]%mo+mo)%mo)%mo;
            }
        }
        ll res = 0;
        for(int i=0;i<n;i++)
            res = (res+(pow(2*c[i],mo-2,mo)%mo)*pow(a[i],mo-2,mo)%mo)%mo;
        cout << res << endl;
    }
    return 0;
}

C. Euclidean Distance

题目

已知n个数构成的数列 { a } \{a\} {a},求n个数构成的数列 { p } \{p\} {p},使得 ∑ i = 1 n ( a i m − p i ) 2 \sum\limits_{i=1}^{n}\left(\frac{a_i}{m}-p_{i}\right)^{2} i=1n(maipi)2最小。
其中

  1. p 1 , p 2 , … , p n ≥ 0 p_{1}, p_{2}, \dots, p_{n} \geq 0 p1,p2,,pn0
  2. p 1 + p 2 + ⋯ + p n = 1 p_{1}+p_{2}+\cdots+p_{n}=1 p1+p2++pn=1

题解

首先,分子分母同时乘m,为了方便计算。

要使 ( a i − p i ) 2 (a_i-p_i)^2 (aipi)2最小,就是让 a i a_i ai缩小一点,如果一个 a j ≤ a i a_j \leq a_i ajai,那么它的缩小量 Δ \Delta Δ也一定比 a i a_i ai小,这时候,相同大小的p,我们就让个 a i a_i ai去缩小。

如果要让 a i 和 a j a_i和a_j aiaj两个值缩小后达到最优,那么一定是将这两个都缩小成同一个值(显然),如果不缩小成同一个值的话,就违背了上一条原则。

同样的,对于n个数,先将a数列从大到小排序,再将前k个缩小成同一值(要求k尽量大),这时候p已经分完了,剩下的就不缩小了, p i p_i pi都等于0。

代码

#include
using namespace std;
 
const int maxn = 10005;
typedef long long ll;
template <class T>
void read(T &x) {
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0'; s=getchar();}
    x *= f;
}
 
ll n,m,a[maxn],sum[maxn],k,p,q;
 
ll gcd(ll a,ll b) {
    if(!b) return a;
    return gcd(b,a%b);
}
 
int main() {
    while(~scanf("%lld%lld",&n,&m)) {
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++) read(a[i]);
        sort(a+1,a+n+1,greater<int>());
        for(int i=1;i<=n;i++) sum[i] = sum[i-1] + a[i];
        k = n; //如果全满足,那么下面循环不会记录
        for(int i=1;i<=n;i++) {
            if(sum[i]-a[i+1]*i>m) {
                k = i;
                break;
            }
        }
        p = (sum[k]-m)*(sum[k]-m)*k;
        q = k*k;
        for(int i=k+1;i<=n;i++) {
            p += a[i]*a[i]*q;
        }
        q *= m*m;
        ll gd = gcd(p,q);
        p/=gd, q/=gd;
        if(q==1 || !p) cout<<p<<endl;
        else cout<<p<<'/'<<q<<endl;
    }
    return 0;
}

E. ABBA

题目

如果一个长度为2(n+m)的字符串S可以被分为n+m个子序列,其中n个为AB,m个为BA,已知n和m,问最多可以构造出多少个这样的字符串S,最终结果mod 1e9+7。
其中 0 ≤ n , m ≤ 1 0 3 0 \leq n, m \leq 10^{3} 0n,m103

题解

对于一个字符串S,我们考虑能否把它按照题目要求分。
从头向后遍历,如果遇到一个A,那么它必然是AB的A,只有当AB分完了以后才考虑它是BA的A;同理,如果遇到一个B,那么它也一定是BA的B,只有当BA分完了以后才考虑它是AB的B。

于是我们采用DP进行构造,dp[i][j]表示用了i个A,j个B,那么就有状态转移方程:
dp[i+1][j] = dp[i+1][j] + dp[i][j],条件是i

代码

#include
using namespace std;
 
const int maxn = 2005;
const int mo = 1e9+7;
typedef long long ll;
template <class T>
void read(T &x) {
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0'; s=getchar();}
    x *= f;
}
 
int n,m;
 
int main() {
    while(~scanf("%d%d",&n,&m)) {
        ll dp[n+m+5][n+m+5];
        memset(dp,0,sizeof(dp));
        dp[0][0] = 1;
        for(int i=0;i<=n+m;i++) {
            for(int j=0;j<=n+m;j++) {
                if(i<n || (i-n)<min(j,m))
                    dp[i+1][j] = (dp[i+1][j]+dp[i][j])%mo;
                if(j<m || (j-m)<min(i,n))
                    dp[i][j+1] = (dp[i][j+1]+dp[i][j])%mo;
            }
        }
        cout << dp[n+m][n+m] << endl;
    }
    return 0;
}

F. Random Point in Triangle

题目

已知三角形三个点A、B、C的坐标,现在在三角形内随机选一个点P, E = max ⁡ { S P A B , S P B C , S P C A } E=\max \left\{S_{P A B}, S_{P B C}, S_{P C A}\right\} E=max{SPAB,SPBC,SPCA},求出E的期望。将结果*36,保证其是整数。

题解

随机数撒个点就求出来了。

代码

#include
using namespace std;
  
const int maxn = 200005;
  
typedef long long ll;
template <class T>
void read(T &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0'; s=getchar();}
    x *= f;
}
  
ll a1,a2,b1,b2,c1,c2;
 
int main() {
    while(cin>>a1>>a2>>b1>>b2>>c1>>c2) {
        ll px = b1-a1;
        ll py = b2-a2;
        ll qx = c1-a1;
        ll qy = c2-a2;
        ll s = abs(px*qy-py*qx);
        cout << s*11 << endl;
    }
    return 0;
}

J. Fraction Comparision

题目

比较 x a \frac{x}{a} ax y b \frac{y}{b} by的大小。
其中 : 0 ≤ x , y ≤ 1 0 18 0 \leq x, y \leq 10^{18} 0x,y1018 1 ≤ a , b ≤ 1 0 9 1 \leq a, b \leq 10^{9} 1a,b109

题解

直接比会溢出。可以选择用Python。
如果用C++的话,把 x a \frac{x}{a} ax写成 ⌊ x a ⌋ + x   m o d   a a \left\lfloor\frac{x}{a}\right\rfloor+\frac{x \bmod a}{a} ax+axmoda,然后先比较取整的部分,如果相等的话,取模的部分再交叉相乘即可。

代码

while 1:
    try:
        x,a,y,b=input().split()
        x = int(x)
        y = int(y)
        a = int(a)
        b = int(b)
        if x*b==y*a:
            print("=")
        elif x*b<y*a:
            print("<")
        else:
            print(">")
    except:
        break
#include
using namespace std;
  
const int maxn = 200005;
typedef long long ll;
 
template <class T>
void read(T &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0'; s=getchar();}
    x *= f;
}
 
ll a,b,x,y;
 
int main() {
    while(cin>>x>>a>>y>>b) {
        ll p = x/a;
        ll q = y/b;
        if(p<q) puts("<");
        else if(p>q) puts(">");
        else {
            x = x%a*b;
            y = y%b*a;
            if(x>y) puts(">");
            else if(x<y) puts("<");
            else puts("=");
        }
    }
    return 0;
}

你可能感兴趣的:(牛客网暑期多校训练赛)