2019牛客多校第一场
A:Equivalent Prefixes(单调栈)
- 题意:注意是每个子区间都要满足
- 可以发现必须要有单调性,想到要同增同减
- 然后找到一个满足同增同减,但是不符合题意的反例:
- 1 3 2
- 1 3 0
- 然后发现必须要维护一个以当前点结尾的最长上升子序列长度相同
- 如果不同就break
- 就想到单调栈 不过我不会严格证明,比赛的时候找不到反例
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const int N = 1e5 + 10;
int a[N], b[N];
int q1[N], q2[N];
int main() {
int n;
while (scanf("%d", &n) != EOF) {
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)scanf("%d", &b[i]);
int l1 = 0, r1 = 0, l2 = 0, r2 = 0;
int ans = 1;
for (int i = 1; i <= n; i++) {
while (l1 <= r1 && q1[r1] > a[i]) {
r1--;
}
q1[++r1] = a[i];
while (l2 <= r2 && q2[r2] > b[i]) {
r2--;
}
q2[++r2] = b[i];
if ((r1 - l1) == (r2 - l2))ans = i;
else break;
}
printf("%d
", ans);
}
return 0;
}
B: Integration(数学)
- 题意:求那个积分
- 裂项
- 把乘换成减,两项两项地换
- 复杂度(O(n^2))
- 积分的可加性
- (pre)记录系数,最后再乘以积分的值(pi/{2a})
- 代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 1e9 + 7;
ll s[1010];
ll pre[1010];
ll qpow(ll a, ll n) {
ll res = 1;
a %= mod;
while (n) {
if (n & 1) res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
int main() {
int n;
while (scanf("%d", &n) != EOF) {
for (int i = 1; i <= n; ++i) scanf("%lld", &s[i]), pre[i] = 0;
pre[1] = 1;
for (int i = 2; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
pre[j] = (pre[j] * qpow(((s[i] * s[i] % mod - s[j] * s[j] % mod)%mod + mod) % mod,mod-2)+mod)% mod;
pre[i] = ((pre[i] - pre[j]) % mod + mod) % mod;
}
}
ll ans = 0;
for (int i = 1; i <= n; ++i) {
ans = ((ans + pre[i] * qpow(s[i] * 2LL, mod - 2) % mod+mod)%mod+mod) % mod;
}
printf("%lld
", ans);
}
return 0;
}
C:Euclidean Distance(数学+贪心)
-
题意:题意比较明显,P的和是1,然后给出A,求那个最小值.
-
数学:题解的证明就看懂第一行
-
贪心:先把P转换一下,去a的分母让P的和为m
-
可以看这个大牛的博客:
-
https://blog.nowcoder.net/n/1539da6d6d6e47a6998b5c6f5bba2167
-
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 1e4 + 10;
ll s[maxn];
ll pre[maxn];
int main() {
int n, m;
while (scanf("%d%d", &n, &m) != EOF) {
for (int i = 1; i <= n; ++i) {
scanf("%lld", &s[i]);
}
sort(s + 1, s + 1 + n);
reverse(s + 1, s + 1 + n);
pre[0] = -m;
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1] + s[i];
}
ll pos = n;
for (ll i = 0; i < n; ++i) {
if (pre[i] > s[i + 1] * i) {
pos = i;
break;
}
}
ll ansa = pre[pos] * pre[pos] * pos;
ll ansb = pos * pos;
for (ll i = pos + 1; i <= n; ++i) {
ansa += s[i] * s[i] * ansb;
}
ansb *= (m * m);
ll g = __gcd(ansa, ansb);
ansa /= g;
ansb /= g;
if (ansb == 1) {
printf("%lld
", ansa);
} else {
printf("%lld/%lld
", ansa, ansb);
}
}
return 0;
}
E:ABBA(贪心+DP)
-
题意:就是有(n)个"AB",(m)个"BA",问能结合成多少个序列.这个要求是AB和BA的顺序不变,即A和B的相对位置不变.我们要讨论一下什么才是合法的状态.
-
贪心:
- 假设只有(n)个AB,合法情况是每个B前面要有(1 ...n)个A
- 假设除了有AB,还有(m)个BA,那每个B前面可以有超过(n)个A,但是第一个B前面还是要有(1...n)个A.否则就会使BA类型的某个B后面没A.
- B与后面的A可以构成BA,相当于抵消了一个A,那下一个B前面就只需要有(1...n)未被抵消的A.
- 所以A-B小于等于(n)是合法的.当A-B等于(n),意味着最后一个只能是A,因为如果最后一个是B,那B前面就有(n+1)个未被抵消的A.
- A前面有多少个B也是同理.
-
DP
-
(dp[i][j]),(i)代表A的个数,(j)代表B的个数.(dp)值代表合法方案.
-
初始化,按照上述前缀的限定
-
(dp[i][0]=dp[0][j]=1)
-
不考虑那么多,可以简单得出
[dp[i][j]=dp[i-1][j]+dp[i][j-1] ] -
但是要排除非法的情况
- (0leq j-ileq m)和(0leq i-j leq n) 是合法情况
- 注意(=m)和(=n)和(=0)的情况
-
-
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const ll N = 1e5 + 10;
ll dp[2010][2010];
int main() {
ll n, m;
while (scanf("%lld%lld", &n, &m) != EOF) {
ll cnt = (n + m);
for (int i = 0; i <= cnt; i++)
for (int j = 0; j <= cnt; j++)
dp[i][j] = 0;
for (ll i = 0; i <= cnt; i++) {
dp[i][0] = dp[0][i] = 1;
}
for (ll i = 1; i <= cnt; i++) {
for (ll j = 1; j <= cnt; j++) {
if (i == j) {
if (n)
dp[i][j] = (dp[i][j] + dp[i][j - 1]) % mod;
if (m)
dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod;
} else if (j > i) {
if (j - i < m)
dp[i][j] = ((dp[i][j] + dp[i - 1][j]) % mod + dp[i][j - 1]) % mod;
else if (j - i == m)
dp[i][j] = (dp[i][j] + dp[i][j - 1]) % mod;
} else if (i > j) {
if (i - j < n)
dp[i][j] = ((dp[i][j] + dp[i - 1][j]) % mod + dp[i][j - 1]) % mod;
if (i - j == n)
dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod;
}
//cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
}
}
printf("%lld
", dp[cnt][cnt] % mod);
}
return 0;
F:Random Point in Triangle(期望+随机+叉积)
参考了一下大牛的博客
首先我们得出当一个点P随机落在三角形内部,新三角形BPC的高的期望为(H/3)
证明如下:(字丑勿怪)
得到了这个,那就好办了,就变成了简单几何问题.
剩下的可以看大佬的博客,我就不赘述了.
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const ll N = 1e5 + 10;
int main() {
ll x1, y1, x2, y2, x3, y3;
while (~scanf("%lld%lld%lld%lld%lld%lld", &x1, &y1, &x2, &y2, &x3, &y3)) {
ll ans = 11;
ll cnt = abs(x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2);
ans = ans * cnt;
printf("%lld
", ans);
}
return 0;
}
Fraction Comparision(大数+JAVA)
居然不会JAVA的多组输入,JAVA白考98了…..
一开始用了nextline 一直RE.
import java.math.*;
import java.util.*;
public class Main {
static BigInteger ans,m;
static BigInteger[] arr;
static int n;
public static void main(String []args){
Scanner cin = new Scanner(System.in);
//Long x,a,y,b;
BigInteger x,a,y,b;
while(cin.hasNextBigInteger()){
x=cin.nextBigInteger();
a=cin.nextBigInteger();
y=cin.nextBigInteger();
b=cin.nextBigInteger();
int s=b.multiply(x).compareTo(a.multiply(y));
if(s>0){
System.out.println(">");
}else if(s==0){
System.out.println("=");
}else{
System.out.println("<");
}
}
}
}