2021年度训练联盟热身训练赛第三场(EFL除外)
A Circuit Math(stack)
后缀表达式,用stack解决
AC代码:
#include <bits/stdc++.h>
using namespace std;
map<char, int> mp;
stack<int> st;
char c, d;
int n, len;
int a, b;
string s;
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; ++i)
{
scanf(" %c", &d);
c = 'A' + i;
if(d == 'T') mp[c] = 1;
else mp[c] = 0;
}
getchar();
getline(cin, s);
len = s.size();
for(int i = 0; i < len; i += 2)
{
if(s[i] >= 'A' && s[i] <= 'Z') st.push(mp[s[i]]);
else if(s[i] == '*')
{
a = st.top();
st.pop();
b = st.top();
st.pop();
st.push(a & b);
}
else if(s[i] == '+')
{
a = st.top();
st.pop();
b = st.top();
st.pop();
st.push(a | b);
}
else if(s[i] == '-')
{
a = st.top();
st.pop();
st.push(!a);
}
}
if(st.top()) putchar('T');
else putchar('F');
return 0;
}
B Diagonal Cut(规律)
求两数的gcd,然后约分,约分后得到两个数,若两个数都是奇数,则答案是gcd,否则答案为0
AC代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <sstream>
#include <map>
#include <set>
using namespace std;
#define inf 0x3f3f3f3f
#define MAX 1000 + 7
#define endl '\n'
//#define mod 571373;
typedef long long ll ;
//不开longlong见祖宗!
//ios::sync_with_stdio(false);
//cin.tie(0); cout.tie(0);
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;
ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
ll gcd(ll a, ll b)
{
if(b) return gcd(b, a % b);
else return a;
}
ll n, m;
int main(){
cin>>n>>m;
ll g = gcd(n, m);
ll x = n / g;
ll y = m / g;
if(x % 2 == 1 && y % 2 == 1)cout<<g<<endl;
else cout<<0<<endl;
cout<<gcd(n , m);
// ll maxn = max(n, m);
// ll minx = min(n, m);
// if(maxn == minx)cout<<minx<<endl;
// else if(maxn % minx == 0){
// if(maxn / minx % 2 == 1)cout<<minx<<endl;
// else cout<<0<<endl;
// }
// else {
// ll x = m / gcd(n, m);
// ll ans = m / x;
// cout<<ans<<endl;
// }
//
return 0;
}
C Gerrymandering
背景是美国总统大选,先输入两个数n,m代表有n次计票,有m个州参加投票,如果一共投了300票,那么谁先得到151张谁就赢了,假设拜登得了200票,建国同志得了100票,那么拜登得到票种有49张是无效的(因为只需要151张就可以惹),建国同志这100张都是无效的,然后有个量好像叫什么效率,公式是这样的wa,wb分别代表拜登和建国同志得到的无效票,v代表总票数。
之后有n行输入,每行三个数,第一个代表这是哪个州,后面两个数分别是拜登和建国同志的的票。
对于输出,要求输出m + 1行,第i行输出三个东西,第一个是字符,对于这个州来说,拜登赢了输出A,建国同志赢了输出B,然后分别输出它们两个所浪费的票,最后一行输出那个效率。
看懂题之后很简单,就是题目太长了。。。
AC代码:
#include <bits/stdc++.h>
using namespace std;
int p, D;
struct node
{
int a, b;
int aa, bb;
} ar[1050];
int wa, wb, v;
int d, x, y, st;
int main()
{
scanf("%d%d", &p, &D);
for(int i = 1; i <= p; ++i)
{
scanf("%d%d%d", &d, &x, &y);
ar[d].a += x;
ar[d].b += y;
v += x;
v += y;
}
for(int i = 1; i <= D; ++i)
{
st = (ar[i].a + ar[i].b) / 2 + 1;
if(ar[i].a >= st)
{
printf("A ");
ar[i].aa = ar[i].a - st;
ar[i].bb = ar[i].b;
printf("%d %d\n", ar[i].aa, ar[i].bb);
}
else
{
printf("B ");
ar[i].aa = ar[i].a;
ar[i].bb = ar[i].b - st;
printf("%d %d\n", ar[i].aa, ar[i].bb);
}
wa += ar[i].aa;
wb += ar[i].bb;
//cout << st << endl;
}
double w = fabs(double(wa) - double(wb));
double vv = double(v);
double ans = w / vv;
printf("%.10lf", ans);
return 0;
}
D题 签到题,过
G Research Productivity Index(期望dp)
说这个人写了n票论文,有的交上去能过,有的不能过 ,有一个量叫研究效率指数,它的值是a^(a/s),a代表过的论文,s代表一共交了多少篇,这个人知道每篇论文过的概率,并且它会明智的提交论文,求他这个研究效率指数期望的最大值。
分析:
这个题的题意不是很好理解,首先要求的是期望的最大值,就说明要在不同条件下求好几个研究效率指数,这个不同的条件就是,提交不同的篇数,他又是明智的所以她一定会交通过可能最大的那几篇,比如交了3篇,那他可能过0,1,2,3篇,这对应不同的概率,也对应不同的a值,所以有一个期望。
剩下的问题在于怎么求期望,我们需要知道不同的a^(a/s)对应的概率,如交了3篇,我们需要求出过了0,1,2,3篇的概率,而这需要动态规划,比如交了i篇过了j篇的概率可以由交了i - 1篇过了j篇的概率和交了i - 1篇过了j篇的概率求出。状态转移方程如下:
dp[i][j] = dp[i - 1][j - 1] * ar[i] * 1.0 / 100 + dp[i - 1][j] * (100 - ar[i]) * 1.0 / 100;
除此之外,还需要考虑初始化和边界条件,初始化dp[0][0] = 1,交了0份,过了0分的概率是1,还有一个后界就是交了i篇过了i篇的概率,有一种情况是由交了i - 1篇过了i篇推过来的,很明显这是不可能的,而dp[i - 1][i]一定是0,所以也不会对答案造成影响。
接下来就是分别求出交1~n篇的期望,然后取个最大值即可
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n;
double ar[105];
double ans, temp;
double dp[105][105];
bool cmp(double a, double b)
{
return a > b;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%lf", &ar[i]);
sort(ar + 1, ar + n + 1, cmp);
dp[0][0] = 1;
for(int i = 1; i <= n; ++i) dp[i][0] = dp[i - 1][0] * (100 - ar[i]) / 100.0;
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= i; ++j)
{
dp[i][j] = dp[i - 1][j - 1] * ar[i] * 1.0 / 100 + dp[i - 1][j] * (100 - ar[i]) * 1.0 / 100;
}
}
for(int i = 1; i <= n; ++i)
{
temp = 0;
for(int j = 1; j <= i; ++j)
{
temp += (pow(j, j * 1.0 / i) * dp[i][j]);
}
ans = max(ans, temp);
}
printf("%.9lf\n", ans);
return 0;
}
I Slow Leak(最短路dijstra & floyd)
题意:
有n个点,m条边,t个加油站,还有一个d代表加一次油车最多能走多远,然后输入t个数,代表这t个加油站,然后m行,每行三个数u,v,w代表u和v之间有一条长度为w的路。求出点1到点n的最短路。注意限制条件d,单词开车不加油最多走的距离是d。然后能走到输出最短路,走不到输出stuck
分析:
先跑一篇floyd,得到任意两点的最短路,然后考虑到图中这n个点,有t + 2个点是特殊的(起始点应该不会同时是加油站),这t+2个点是t个加油站和起始点,我们可以建一张只有这t+2个点的图,判断这t +2个点种的任意一个点与其他t + 1个点的最短路是否小于等于d,如果小于就连边。这样就建完图了,这样建的图就不需要考虑那个d的限制了.
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, t, tot;
int u, v;
ll w;
ll d;
ll mp[505][505];
ll dis[505];
int head[505];
bool vis[505];
int tt[505];
struct node
{
int v, w, ne;
} edge[250050];
struct node1
{
int l, pos;
bool operator < (const node1 b) const
{
return l > b.l;
}
};
priority_queue<node1> q;
void Init()
{
tot = 0;
memset(mp, 0x3f, sizeof(mp));
memset(head, -1, sizeof(head));
memset(edge, -1, sizeof(edge));
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
}
void addedge(int u, int v, int w)
{
edge[++tot].v = v;
edge[tot].w = w;
edge[tot].ne = head[u];
head[u] = tot;
}
void floyd() {
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
mp[i][j]=min(mp[i][j], mp[i][k] + mp[k][j]);
}
void dijkstra()
{
struct node1 now, next;
dis[1] = 0;
now.l = 0, now.pos = 1;
q.push(now);
while(!q.empty())
{
now = q.top();
q.pop();
if(vis[now.pos]) continue;
vis[now.pos] = 1;
for(int i = head[now.pos]; i != -1; i = edge[i].ne)
{
next.pos = edge[i].v;
if(dis[next.pos] > dis[now.pos] + edge[i].w)
{
dis[next.pos] = dis[now.pos] + edge[i].w;
next.l = dis[next.pos];
q.push(next);
}
}
}
}
int main()
{
Init();
scanf("%d%d%d%lld", &n, &m, &t, &d);
for(int i = 1; i <= t; ++i) scanf("%d", &tt[i]);
tt[++t] = 1, tt[++t] = n;
for(int i = 1; i <= m; ++i)
{
scanf("%d%d%lld", &u, &v, &w);
mp[u][v] = mp[v][u] = w;
}
floyd();
for(int i = 1; i <= t + 2; ++i)
{
for(int j = i + 1; j <= t + 2; ++j)
{
if(mp[tt[i]][tt[j]] <= d)
{
addedge(tt[i], tt[j], mp[tt[i]][tt[j]]);
addedge(tt[j], tt[i], mp[tt[i]][tt[j]]);
}
}
}
dijkstra();
if(dis[n] >= 0x3f3f3f3f3f3f3f3f) printf("stuck\n");
else printf("%d\n", dis[n]);
//cout << dis[n] << endl;
return 0;
}
J Stop Counting!(前缀和/后缀和)
题意:
给你一坨数,可以删掉连续的k(k可取1~n)个数,请你进行一次删除操作,使得剩下的数的平均数最大。
分析:
这个题,弄明白一个问题即可,就是这又两坨数,一坨的平均数是a,另一坨的平均数是b,把这两坨数合在一起的话平均数绝对不会大于max(a,b),所以没必要在中间删除k个数,就是在开头删除一坨数,或者在结尾删除一坨数,所以做法是求前缀和以及后缀和,然后遍历一遍前缀和数组和后缀和数组,求平均值并取max
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll ar[1000050], br[1000050], cr[1000050];
double ans, ans1, ans2;
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%lld", &ar[i]);
br[i] = br[i - 1] + ar[i];
}
for(int i = n; i > 0; --i)
{
cr[i] = cr[i + 1] + ar[i];
ans2 = (double)cr[i] / (n - i + 1);
ans1 = (double)br[i] / i;
ans = max(ans, max(ans1, ans2));
}
printf("%.10lf\n", ans);
return 0;
}
K Summer Trip(字符串构造)
按照他说的构造即可,跑两层for循环(不会超时,因为第二层跑不了太多次,会break的),然后可以预处理优化一下,对于字符串aabbbdddfff,预处理为abdf。
AC代码:
#include <bits/stdc++.h>
using namespace std;
string s;
string ss;
char la;
int len, ans;
bool vis[30];
int main()
{
cin >> s;
len = s.size();
la = s[0];
ss += la;
for(int i = 1; i < len; ++i)
{
if(la == s[i]) continue;
else ss += s[i];
la = s[i];
}
len = ss.size();
for(int i = 0; i < len; ++i)
{
memset(vis, 0, sizeof(vis));
for(int j = i + 1; j < len; ++j)
{
if(ss[j] == ss[i]) break;
else if(!vis[(int)ss[j] - 96])
{
ans++;
vis[(int)ss[j] - 96] = 1;
}
}
}
printf("%d\n", ans);
return 0;
}
M Zipline(数学 /思维/二分)
最小值,不用说
那个最大最小值,就算当时没看懂题,
1.应该瞎验证样例,也能发现是一个点对称下去
2.求出表达式,既然是最值,求导,二分导数等于0的点,根本不用管这是个极大值还是极小值,直接带就行,反正要么是导数为0点,要么是端点。
3.应该意识到这是一个特殊的点,两边的点都应该比这个点处取值大或小,所以就是看不懂题应该要瞎猜到袜,呜呜呜 ~~~
AC代码:
对称做法:
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <sstream>
#include <map>
#include <set>
using namespace std;
#define inf 0x3f3f3f3f
#define MAX 1000000 + 7
#define endl '\n'
//#define mod 571373;
typedef long long ll ;
//不开longlong见祖宗!
//ios::sync_with_stdio(false);
//cin.tie(0); cout.tie(0);
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;
ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
int n;
double a, b, c, d, minx, maxn;
int main(){
cin>>n;
while (n--) {
cin>>a>>b>>c>>d;
minx = sqrt(a * a + (b - c)*(b - c));
maxn = sqrt(a * a + (b + c - 2 * d) * (b + c - 2 * d));
printf("%.8f %.8f\n",minx, maxn);
}
}
函数求导做法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
int n;
cin >> n;
while (n--) {
ll w, g, h, r; // 注意数据范围,平方会爆int
cin >> w >> g >> h >> r;
// 求长度的最小值:l = min(g,h),且人处于高度较低处
double mi = sqrt((w * w) + (h - g) * (h - g));
// 求长度的最大值: l = r,二分找出导数为0的点
g = g - r;
h = h - r;
double L = 0, R = w;
while (R - L > 1e-8) // 设置精度一般比题目要求多两位
{
double x = (R + L) / 2;
double f = x / sqrt(g * g + x * x) - (w - x) / sqrt(h * h + (w - x) * (w - x));
if (f < 0) L = x;
else R = x;
}
// 带回原方程求解
double ma = sqrt(g * g + L * L) + sqrt(h * h + (w - L) * (w - L));
printf("%.8lf %.8lf\n", mi, ma);
}
}