题解 | #Amino Acids#
Amino Acids
https://ac.nowcoder.com/acm/contest/32708/A
2022ICPC昆明补题(第46届ICPC亚洲区域赛(昆明))
A.Amino Acids
题解:挺好玩的大模拟,化学知识得到了提升,属于那种看懂题就能做的类型,平时写得太少导致赛时出锅。
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
map<string,int> mmap;
int num[] = {0,89,132,133,121,146,147,75,149,105,119};
string s[N];
int _min = 10000000;
vector<string> vec[N];
string t1 = " H H O ";
string t2 = " | | || ";
string t3 = "H-N-C-C-O-H";
string t4 = " | ";
string t5 = " H-C-H ";
string t6 = " O=C-O-H ";
string t7 = " H ";
string t8 = " | ";
string t9 = " H ";
string t10= " S ";
string t11= " H-C-S-H ";
string t12= " ";
string t13= " H-C-O-H ";
string t14= " O=C-N-H ";
int n,sz;
void f(){
for(int i=1;i<=10;i++){
vec[i].push_back(t1);vec[i].push_back(t2);vec[i].push_back(t3);vec[i].push_back(t4);
}
vec[1].push_back(t5);vec[3].push_back(t5);
vec[2].push_back(t5);vec[8].push_back(t5);
vec[5].push_back(t5);vec[6].push_back(t5);
vec[1].push_back(t4);vec[1].push_back(t7);
vec[3].push_back(t4);vec[3].push_back(t6);
vec[2].push_back(t4);vec[2].push_back(t14);vec[2].push_back(t8);vec[2].push_back(t9);
vec[4].push_back(t11);vec[4].push_back(t4);vec[4].push_back(t7);
vec[7].push_back(t7);
vec[9].push_back(t13);vec[9].push_back(t4);vec[9].push_back(t7);
vec[8].push_back(t4);vec[8].push_back(t5);vec[8].push_back(t4);vec[8].push_back(t10);
vec[8].push_back(t4);vec[8].push_back(t5);vec[8].push_back(t4);vec[8].push_back(t7);
vec[10].push_back(t13);vec[10].push_back(t4);vec[10].push_back(t5);vec[10].push_back(t4);
vec[10].push_back(t7);
vec[5].push_back(t4);vec[5].push_back(t5);vec[5].push_back(t4);
vec[5].push_back(t14);vec[5].push_back(t8);vec[5].push_back(t9);
vec[6].push_back(t4);vec[6].push_back(t5);
vec[6].push_back(t4);vec[6].push_back(t6);
}
vector<int> a;
vector<int> res;
int ans = 0;
void print(int n){
int _max = 0;
for(int i=0;i<n;i++){
_max = max(_max,(int)vec[res[i]].size());
}
for(int i=0;i<_max;i++){
if(i==2)
for(int j=0;j<n;j++){
int l = 0, r = vec[res[j]][i].size()-1;
l++;r-=3;
if(j==0)l--;if(j==n-1)r+=3;
if(j!=0)cout << "-";
for(int k = l;k<=r;k++)cout << vec[res[j]][i][k];
}
else {
for(int j=0;j<n;j++){
int l = 0;int r;
if(vec[res[j]].size() > i)r = vec[res[j]][i].size()-1;
l+=2;r--;
if(j==0)l-=2;
if(j==n-1)r++;
//if(res[j]==7)cout << vec[res[j]].size() << " " << i << endl;
if(vec[res[j]].size() <= i){
r = 9;
for(int k=l;k<=r;k++)cout << " ";
}
else for(int k=l;k<=r;k++)cout << vec[res[j]][i][k];
}
}
cout << endl;
}
cout << endl;
}
void dfs2(int cur,int sum){
if(sum > sz)return;
if(cur>=2){
ans++;
}
//if(cur==n)return;
for(int i=0;i<n;i++){
if(cur==0)dfs2(cur+1,sum+num[a[i]]);
else dfs2(cur+1,sum+num[a[i]]-18);
}
return;
}
void dfs(int cur,int sum){
if(sum > sz)return;
if(cur>=2){
print(cur);
}
//if(cur==n)return;
for(int i=0;i<n;i++){
res.push_back(a[i]);
if(cur==0)dfs(cur+1,sum+num[a[i]]);
else dfs(cur+1,sum+num[a[i]]-18);
res.pop_back();
}
return;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
f();
//cout << vec[7].size() << endl;
mmap["Ala"] = 1;
mmap["Asn"] = 2;
mmap["Asp"] = 3;
mmap["Cys"] = 4;
mmap["Gln"] = 5;
mmap["Glu"] = 6;
mmap["Gly"] = 7;
mmap["Met"] = 8;
mmap["Ser"] = 9;
mmap["Thr"] = 10;
cin >> n >> sz;
for(int i=1;i<=n;i++){
string s;cin >> s;
a.push_back(mmap[s]);
}
// for(int i=0;i<n;i++){
// for(auto it:vec[a[i]]){
// cout << it << endl;
// }
// cout << endl;
// }
sort(a.begin(),a.end());
dfs2(0,0);
cout << ans << endl;
dfs(0,0);
return 0;
}
B.Blocks
题解:期望dp,n的规模非常小,一个小trick是处理自环的情况,通过移项移到同一边就可以处理了,在判断全部覆盖可以使用容斥定理或者离散化。离散化注意一些边界问题,而且这题数据略水。
//离散化
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(b)-1;i>=a;i--)
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define VI vector<int>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const int N = 15;
ll qpow(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
#define y1 awdjawif
int x1[N],y1[N],x2[N],y2[N];
ll s[1<<N],ns[1<<N],dp[1<<N];
ll inv[20];
bool st[40][40];
vector<int> xx;
vector<int> yy;
int f(int x,VI xx){
return lower_bound(all(xx),x) - xx.begin();
}
int n;
void init(){
sort(all(xx));
sort(all(yy));
xx.erase(unique(all(xx)),xx.end());
yy.erase(unique(all(yy)),yy.end());
int szx = xx.size();
int szy = yy.size();
rep(S,0,1<<n){
ns[S] = 0;
int cnt = 0;
rep(i,0,szx)rep(j,0,szy)st[i][j] = 0;
rep(j,0,n){
if(((1<<j)&S)){
int X1 = f(x1[j],xx),Y1 = f(y1[j],yy);
int X2 = f(x2[j],xx),Y2 = f(y2[j],yy);
rep(a,X1,X2)rep(b,Y1,Y2){
if(!st[a][b]){cnt++;st[a][b] = 1;}
}
}
}
if(cnt == (szx-1) * (szy-1))ns[S] = 1;
}
}
int main(){
//cout << qpow(3, 5);
for(int i=1;i<=11;i++)inv[i] = qpow(i,mod-2);
int T;scanf("%d",&T);
while(T--){
xx.clear();
yy.clear();
scanf("%d",&n);
int W,H;
scanf("%d%d",&W,&H);
rep(i,0,n){
scanf("%d%d%d%d",x1+i,y1+i,x2+i,y2+i);
xx.pb(x1[i]);xx.pb(x2[i]);
yy.pb(y1[i]);yy.pb(y2[i]);
}
xx.pb(0);xx.pb(W);
yy.pb(0);yy.pb(H);
init();
//ll ss = 1ll * W * H;
if(!ns[(1<<n)-1]){
cout << -1 << endl;continue;
}
//for(int i=100;i<150;i++)cout << ns[i] << " ";
// cout << endl;
// cout << ns[1] << endl;
// cout << ns[4] << endl;
// cout << ns[15+128] << endl;
per(i,0,1<<n){
if(ns[i]){
dp[i] = 0;
//cout << i << endl;
}
else {
//ll sz = n;
int c = 0;
long long t = 1;
rep(j,0,n){
if(((1<<j)&i)==0){
t = (t + inv[n] * dp[i|(1<<j)] % mod)%mod;
c++;
}
}
dp[i] = (ll)t * n % mod * inv[c] % mod;
}
}
cout << dp[0] << endl;
//cout << dp[(1<<n)-2] << endl;
}
}
//容斥原理
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(b)-1;i>=a;i--)
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const int N = 15;
ll qpow(ll a,ll b){ll t = 1;assert(b>=0);for(;b;b>>=1){if(b&1)t=(ll)t*a%mod;a=(ll)a*a%mod;}return t;}
#define y1 awdjawif
int x1[N],y1[N],x2[N],y2[N];
ll s[1<<N],ns[1<<N],dp[1<<N];
ll inv[20];
signed main(){
//cout << qpow(3, 5);
for(int i=1;i<=10;i++)inv[i] = qpow(i,mod-2);
int T;scanf("%lld",&T);
while(T--){
int n;scanf("%lld",&n);
int W,H;
scanf("%lld%lld",&W,&H);
rep(i,0,n){
scanf("%lld%lld%lld%lld",x1+i,y1+i,x2+i,y2+i);
}
rep(i,1,1<<n){
int X1 = 0,Y1 = 0,X2 = W,Y2 = H;
rep(j,0,n){
if(i&(1<<j)){
X1 = max(X1,x1[j]);
X2 = min(X2,x2[j]);
Y1 = max(Y1,y1[j]);
Y2 = min(Y2,y2[j]);
}
}
s[i] = 1ll * max(0ll,X2-X1) * max(0ll,Y2-Y1);
//cout << s[i] << endl;
}
rep(i,1,1<<n){
ll v = 0;
for(int j=i;j;j=(j-1)&i){
v += s[j] * (__builtin_parity(j)?1:-1);
}
ns[i] = v;
}
//cout << ns[(1<<n)-1] << endl;
if(ns[(1<<n)-1] < 1ll * W * H){
cout << -1 << endl;continue;
}
// cout << ns[1] << endl;
// cout << ns[4] << endl;
per(i,0,1<<n){
if(ns[i]==1ll * W*H){
dp[i] = 0;
//cout << i << endl;
}
else {
//ll sz = n;
long long t = 1;
int c = 0;
rep(j,0,n){
if(((1<<j)&i)==0){
t = (t + inv[n] * dp[i|(1<<j)] % mod)%mod;
c++;
}
}
dp[i] = (ll)t * n % mod * inv[c] % mod;
}
}
cout << dp[0] << endl;
//cout << dp[(1<<n)-2] << endl;
}
}
C. Cup of Water
题意:还是数学期望题,这套题有三个期望题(合理怀疑出题人在准备概率论考试),有点太多了。题意是给你一个容量为的容器,每一轮等概率取的水。问装满单位为1的水杯需要的期望轮数。
题解:正解没咋看懂,这里是看dls代码得来的一个挺显然的思路。
我们先进行一个小变换,可以转换成以下情况,我们每次等概率取0到1的水,装满的水杯。 我们定义的含义是装满容量为x的水杯所需要的期望轮数,则问题变为求。
根据概率论的基础知识,可以得到,正解就是吧这个求出来,但这个式子求出后不是个初等形式,所以使用了大量的技巧,在高数基础不够的情况下,我们该怎么办呢?对,没错,就是暴力,虽然他是个连续的式子,但是我们可以想起微积分的切片法,这样就可以把连续的式子分割成离散的,因为这道题对精度的要求比较低,实测把单位1分割成份就可以达到题目需要的精度要求。还有一个问题就是我们发现离散后可能为0,易得,但这个显然不对,我们根据题意可以得到,只要不为,那么期望必然不为,所以离散化得注意和取一个。
#include <bits/stdc++.h>
using namespace std;
#define db double
const int dx=300000;
double f[dx*20+10],s[dx*20+10];
int main() {
f[0]=0;
s[0]=0;
for(int i=1;i<20*dx+2;i++) {
f[i]=1.0+(s[i-1]-s[max(i-dx,0)])/((db)dx);
s[i]=s[i-1]+f[i];
}
int _;
for (scanf("%d",&_);_;_--) {
db x;
scanf("%lf",&x);
printf("%.16lf\n",f[max(int(dx/x),1)]);
}
}
D.Divisions
题意:让你构造一个序列,使得这个序列有k种划分,使得划分分别递增和递减。
题解:0,1特判,对于k大于等于2的情况,可以发现如果序列如同111223333,则k为,那个另外的1是空集的情况,我们可以发现通过构造出答案。
#include<bits/stdc++.h>
using namespace std;
vector<int> res;
int main(){
int k;
cin >> k;
if(k==0)cout << "5" << endl << "2 3 1 5 4" << endl;
else if(k==1)cout << "6" << endl << "1 1 4 5 1 4"<< endl;
else {
k -- ;
int ind = 1;
while(k){
int cnt = 0;
for(;;cnt++){
if((1 << (cnt + 1)) - 1> k)break;
}
k -= (1<<cnt)-1;
while(cnt--)res.push_back(ind);
ind ++;
}
cout << res.size() << endl;
for(int i=0;i<res.size();i++){
cout << res[i] << " ";
}
cout << endl;
}
}
E. Easy String Problem
题意:好懒的人啊,自己看题~~~
题解:这道题正解是莫队,那么我们怎么可以想到莫队的呢?有以下几个特征点可以指引我们想到莫队,首先是数据规模,这个其实对应的算法是不多的,比如莫队,分块,块状链表等等,第二是区间询问是莫队的典型特征。其实根据数据规模能得到很多信息的,毕竟出题人也不傻~~~。
我们由此可以得到一个初步判断,他就是莫队,基于这个去思考这道题的性质将会容易很多。 看到这不妨停下来自己想想,莫队需要哪些性质,而我们怎么挖掘与其相关的性质?
不知道聪明的你想到了没有,其实很简单,观察12321,我们假设询问区间是3 3,我们使用正难则反的思路,左边的坐标有三个选择,右边同样是三个,所以可以得到这个答案,我们考虑如何去重,可以发现,删去2 3和3 4一样,1 3和3 5一样,发现好像与区间两旁的重复部分有关系,再比如11312,同样询问3 3,答案同样是 ,我们可以发现和两边重复数字的个数乘积有关系,没错一个询问区间答案就是全部取法减去两边重复数字个数的乘积。我们通过莫队可以轻松维护~~~
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define ll long long
const int N = 1e5+5;
int a[N];
int len;
ll ans[N];
int n,m;
struct Query{
int id,l,r;
}q[N];
int get(int x){
return x/len;
}
int cntr[N];
int cntl[N];
void addr(int x,ll& quan){
cntr[x]--;
quan -= cntl[x];
}
void delr(int x,ll& quan){
cntr[x]++;
quan += cntl[x];
}
void addl(int x,ll& quan){
cntl[x]--;
quan -= cntr[x];
}
void dell(int x,ll& quan){
cntl[x]++;
quan += cntr[x];
}
int main(){
cin >> n;
len = max(1,(int)sqrt(n));
for(int i=1;i<=n;i++)cin >> a[i];
cin >> m;
rep(i,0,m){
int l,r;cin >> l >> r;
q[i] = {i,l,r};
}
sort(q,q+m,[&](Query a,Query b){
int i = get(a.l), j = get(b.l);
if(i!=j)return i < j;
return a.r < b.r;
});
int L = 1, R = n;
ll quan = 0;
rep(i,0,m){
int l = q[i].l,r = q[i].r,id = q[i].id;
while(R < r)addr(a[++R],quan);
while(R > r)delr(a[R--],quan);
while(L > l)addl(a[--L],quan);
while(L < l)dell(a[L++],quan);
//cout << quan << " " << l << " " << r << endl;
ans[id] = 1ll * l * (n + 1 - r) - quan;
}
rep(i,0,m){
printf("%lld\n",ans[i]);
}
}
F. Find the Maximum
题解:另外一道签到题,推一推可以发现,所求值的最大就是选定结点的平均值的平方除以4,根据平均的性质,可以判断最多选3个点,然后跑所有情况取最大即可。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define db long double
#define all(x) x.begin(),x.end()
const int N = 1e5+5;
db a[N];
db f(db t){return t*t/4.0;}
vector<int> G[N];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%Lf",a+i);
}
db res = 0;
rep(i,0,n-1){
int u,v;
scanf("%d%d",&u,&v);
res = max(res,f((a[u]+a[v])/2.0));
G[u].push_back(v);G[v].push_back(u);
}
for(int i=1;i<=n;i++){
sort(all(G[i]),[&](int ii,int j){
return a[ii] > a[j];
});
}
for(int i=1;i<=n;i++){
int sz = G[i].size() - 1;
if(G[i].size()>=2){
res = max(res, f((a[i]+a[G[i][0]]+a[G[i][1]])/3.0));
res = max(res, f((a[i]+a[G[i][sz]]+a[G[i][sz-1]])/3.0));
}
}
cout << fixed << setprecision(18) << res << endl;
}
G.Glass Bead Game
题解:又是一个期望题,考虑多个太麻烦,不妨只考虑两个之间的关系,则i在j前面的概率为,其它的是不用考虑,因为不影响他们之间的先后关系。通过这个结论,稍加思考就可以得到答案。
#include<bits/stdc++.h>
using namespace std;
const int N = 104;
double p[N];
int main(){
int n;
cin >> n;
for(int i =1;i<=n;i++)cin >> p[i];
double res = 0;
for(int i=1;i<=n;i++){
double tp = 0;
for(int j=1;j<=n;j++){
if(i==j)continue;
tp += p[j]/(p[i]+p[j]);
}
tp *= p[i];
res += tp;
}
cout << fixed << setprecision(12) << res << endl;
}
K.King of Gamers
题意:签到题,答案就在题面上,注意0的特判。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin >> t;
while(t--){
LL n,a,b;
cin >> n >> a >> b;
if(a==0)cout << 1 << endl;
else if(n*a%b==0)cout << n*a/b << endl;
else if(n==1)cout << 1 << endl;
else {
long long y = n*a/b + 1;
if(b*(y-1) <= a*(n-1));
else y--;
cout << y << endl;
}
}
}