A - 3.14
题意
π≈3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
输出 π 精确到小数点第 n 位的结果。
解法
用字符串处理即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include<bits/stdc++.h> using namespace std;
using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__) #define endl '\n'
int read(){ int f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; }
string s="3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679";
int main(){ int n=read(); cout<<s.substr(0,n+2); return 0; }
|
B - Roulette
题意
有 n 个人在预测一个轮盘。一共有 0∼36 共 37 种结果。每个人预测了几个结果,最终结果是 X。请输出所有预测了 X 且预测数量最少的人。
解法
字面意思。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include<bits/stdc++.h> using namespace std;
using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__) #define endl '\n'
int read(){ int f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; }
const int N = 100; vector<pii> a[N];
int main(){ int n=read(); for(int i=1;i<=n;i++){ int c=read(); for(int j=0;j<c;j++){ int x=read(); a[x].push_back({c,i}); } }
int k=read(); sort(all(a[k]));
if(a[k].size()==0){ puts("0"); return 0; } vector<int> ans; int pp = a[k][0].first; for(auto [p,q]:a[k]){ if(p==pp){ ans.push_back(q); } } printf("%lu\n",ans.size()); for(int x:ans){ printf("%d ",x); } puts(""); return 0; }
|
C - Rotate Colored Subsequence
题意
有一个序列,每个元素被染了一个颜色。现在对于每一个颜色,都单独将这个颜色的元素向右旋转一次。请问最后的序列是什么样的?
解法
用一个 vector
数组储存每个颜色的元素,然后分别使用 rotate
进行旋转一次。
最后输出的时候只需要记录这一位原来是什么颜色的,然后找到那个颜色的下一个进行输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include<bits/stdc++.h> using namespace std;
using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__) #define endl '\n'
int read(){ int f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; }
const int N = 2e5+5; vector<char> colors[N]; vector<int> c; char s[N]; int now[N];
int main(){ int n=read(),m=read(); scanf("%s",s); for(int i=0;i<n;i++){ int k=read(); c.push_back(k); colors[k].push_back(s[i]); }
for(int i=1;i<=m;i++){ rotate(colors[i].rbegin(), colors[i].rbegin() + 1, colors[i].rend()); }
for(int i=0;i<n;i++){ putchar(colors[c[i]][now[c[i]]]); now[c[i]]++; } return 0; }
|
D - LOWER
题意
给定一个字符串,要求能够维护如下三种操作:
- 改变一个字符;
- 所有字符变为大写;
- 所有字符变为小写。
解法
维护每一个字符的最后修改时间以及最后一个大小写操作的事件。
如果这个字符在最后一次大小写操作之后被修改了,那么就原样输出,要不然就转换到对应的大小写然后再输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include<bits/stdc++.h> using namespace std;
using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__) #define endl '\n'
int read(){ int f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; }
const int N = 5e5+5; int lastMod[N]; char s[N];
int modT=-1,modType;
int main(){
int n=read(); scanf("%s",s); int m=read(); for(int i=0;i<m;i++){ int t,x; char c; scanf("%d %d %c",&t,&x,&c);
if(t==1){ s[x-1]=c; lastMod[x-1]=i; } else if(t==2){ modT=i;modType=2; } else{ modT=i;modType=3; } }
for(int i=0;i<n;i++){ if(lastMod[i]>modT){ putchar(s[i]); } else{ if(modType==2)putchar(tolower(s[i])); else putchar(toupper(s[i])); } } return 0; }
|
E - Roulettes
题意
有 n 个轮盘,每个轮盘需要 ci 转一次,等概率转出 si,1,si,2,…,si,pi 中的任意一个。
小 T 想要获得 m 点。他可以根据目前获得的点的个数选择策略,请求出他获得 m 点花钱的最小期望?
解法
设 dp[i] 为达到至少 i 点时花钱的最小期望。
假设 dp[i] 从某个价格为 C, 转出来的为 S1,S2,…,SP 转盘转移。
那么就可以写出如下转移表达式:
dp[i]=P∑i=1P(dp[max(0,i−Si)]+C)
直接暴力转移就可以了,复杂度足够。
还有最后一个问题,有可能可以转出来 0,但是如果这样的话,我们转移方程式中就出现了自己,这是我们不希望的。因此,我们考虑进行一些小小的转化。
我们可以把转盘的价格按比例调高,然后把 0 全部删去,这样对于期望是没有影响的。
例:100 元转出 {0,1,2} 的转盘等价于 150 元转出 {1,2} 的转盘。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| #include<bits/stdc++.h> using namespace std;
using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__) #define endl '\n'
int read(){ int f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } template <class T> T read(){ T f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } template <class T> void write(T x){ if(x<0)putchar('-'),x=-x; if(x>9)write(x/10); putchar(x%10+'0'); }
const int N = 105; double dp[N]; vector<int> p[N]; double val[N]; int zerocnt[N]; int main(){ int n=read(),m=read(); for(int i=0;i<n;i++){ val[i]=read(); int c=read(); for(int j=0;j<c;j++){ int x=read(); if(x==0)zerocnt[i]++; else p[i].push_back(x); } val[i] = val[i]*c/(c-zerocnt[i]); } for(int i=1;i<=m;i++){ dp[i] = 1e100; for(int j=0;j<n;j++){ double now = 0; for(int k:p[j]){ now += (dp[max(0,i-k)]+val[j])/p[j].size(); } dp[i]=min(dp[i],now); } }
printf("%f\n",dp[m]); return 0; }
|
F - A Certain Game
题意
n 个人在进行比赛,初始每个人各自一队。
现在有 n−1 场比赛,每场比赛为 ai,bi 所在队伍进行比赛。人数为 p 和 q 的队伍进行比赛,p 胜率为 p+qp,q 胜率为 p+qq。然后 ai 和 bi 所在的队伍进行合并。
求出每个人的胜利场数期望。
解法
考虑对这个比赛过程建立一颗二叉树。建立二叉树时,需要一些额外的节点,使用并查集维护现在一个连通块的根,就可以 O(n) 建出这一颗树。
然后进行一次 dfs 求出每个节点子树内的人数,然后再进行一次 dfs 转移胜利期望场数。
用线性求逆元求出 1∼n 的逆元。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| #include<bits/stdc++.h> using namespace std;
using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__) #define endl '\n'
int read(){ int f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; }
const int N = 2e5+5; const ll MOD = 998244353; int fa[N]; int represent[N]; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } void unite(int u,int v){ fa[find(u)]=find(v); }
vector<int> G[N*2];
void add_edge(int u,int v){ G[u].push_back(v); G[v].push_back(u); }
ll inv[N]; int n; int cnt=1; int sz[N*2]; ll ans[N]; void dfs(int u,int p){ if(u<=n)sz[u]=1; for(int v:G[u]){ if(v!=p){ dfs(v,u); sz[u]+=sz[v]; } } } void dfs2(int u,int p, ll k){ if(G[u].size()==1){ ans[u]=k; return; }
vector<int> children; for(int v:G[u]){ if(v!=p){ children.push_back(v); } } int c1=children[0],c2=children[1]; ll pLeft = (sz[c1])*(inv[sz[c1]+sz[c2]])%MOD; ll pRight = (sz[c2])*(inv[sz[c1]+sz[c2]])%MOD;
dfs2(c1,u,(k+pLeft)%MOD); dfs2(c2,u,(k+pRight)%MOD); }
int main(){ n=read();
inv[1]=1; for(int i=2;i<=n;i++){ inv[i]=(ll)(MOD-MOD/i)*inv[MOD%i]%MOD; }
int now=n; for(int i=1;i<=n;i++){ fa[i]=i; represent[i]=i; } for(int i=1;i<n;i++){ int u=read(),v=read(); int k1=represent[find(u)],k2=represent[find(v)]; int newNode = ++now; add_edge(now,k1); add_edge(now,k2); unite(u,v); represent[find(u)]=newNode; }
dfs(now,now); dfs2(now,now,0);
for(int i=1;i<=n;i++){ printf("%lld ",ans[i]); }
return 0; }
|
G - Amulets
题意
有 n 个怪物,m 个护身符,你有 h 点生命。
怪物由攻击力 a 和类型 b 决定。
你携带了一些护身符,遇到一个怪物,如果你携带了和 b 相同的护身符,那么不用受到伤害,不然要受到 a 点伤害,当你的生命小于等于 0 时就失败了。
请求出携带 0∼m 个护身符时,最多能打败几个怪物?
解法
不妨将问题转化为打败前 i 个怪物至少需要几个护身符。
我们设 Cj 为类型 j 的怪物的攻击力总和。
我们目标就是选出最多的 Cj,使得它们的和小于等于 h。因此我们只需要从更小的 Cj 开始选即可。
对单独一个 i 问题的解决已经很简单了,接下来考虑如何快速对所有的 i 进行计算。事实上,我们不妨考虑两个 i 之间的 Cj 变化,只会变化一个 Ck,然后再用 set
维护当前选和不选的护身符就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include<bits/stdc++.h> using namespace std;
using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__) #define endl '\n' #define pb push_back
int read(){ int f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; }
const int N = 3e5+5; int p[N]; int ans[N];
set<pair<ll,int>> S,T; ll c[N];
int main(){ int n=read(),m=read(); ll h=read(); for(int i=1;i<=m;i++){ S.insert({0,i}); } ll sum = 0; for(int i=1;i<=n;i++){ ll a=read();int b=read(); if(S.erase({c[b],b})){ c[b]+=a; S.insert({c[b],b}); } if(T.erase({c[b],b})){ sum-=c[b]; c[b]+=a; S.insert({c[b],b}); }
while(S.size() && S.begin()->first+sum<h){ sum += S.begin()->first; T.insert(*S.begin()); S.erase(S.begin()); } while(S.size() && T.size() && S.begin()->first<T.rbegin()->first){ sum += S.begin()->first-T.rbegin()->first; auto itS=S.begin(),itT=--T.end(); S.insert(*itT);T.insert(*itS); S.erase(itS);T.erase(itT); }
p[i]=S.size(); }
for(int i=0;i<=m;i++){ int pos = upper_bound(p+1,p+1+n,i)-p-1; printf("%d " ,pos); } return 0; }
|