训练联盟训练赛之 ICPC North America Qualifier Contest 2015
本身寒假的时候说是一天一套Div.2 然后补题写题解的
结果最后题倒是差不多写了 但是题解鸽了(
守望先锋真的好玩()
从今天这套题开始重新捡起来吧
比赛链接:https://www.jisuanke.com/contest/7197?view=challenges

这场比赛写了7个题,后来把A补了。
一开始以为A是个大模拟,结果一位爹和我说只要八十多行,但当时只剩20多分钟了,先WA一发发现有一个样例没过。
最后调完样例的时候想交但是比赛已经结束了qwq
还好最后开放交题的时候交上去没过,不然血亏2333
No more bb, let's go.
Problem A. All about that base
t组输入,输入的格式是 x op y = z.
其中op是'+',‘-’,‘*’,‘/’中的一个,x,y,z是三个w进制下的数(1<=w<=36)
要求我们找到所有的合法的w(1<=w<=36)让输入的等式成立。w=10-35用a-z表示,w=36用0表示,如果找不到这样的w则输出“invalid”。
w是合法的当且仅当:
(1)输入的等式成立
(2)在w进制下,x,y,z三个数都小于2^32-1(注意,这里是要开long long的)(当时比赛的时候WA在这里了。。没看到这个条件)
这里要解释一下1进制,这里的1进制相当于绳结计数法。比如111+111=111111(3个1+3个1=6个1),11*11=1111(2个1*2个1=4个1)。
当然,如果有除1以外的数字存在的话,那显然就不可以是1进制了。
做法:
按照题意模拟就行。
设出现的最大数字位是minn,那这至少就是minn+1进制了(当然1进制需要特判)。然后从minn+1一直到36逐个尝试,保存答案并输出即可。
1进制的特判方法也很简单,只要遍历字符串,如果字符串里面除了运算符只有1的话,才可能会是1进制。
具体实现看代码吧:
ps:我也不知道getline(cin,s)为什么会把第一个读入吃掉变成空串,反正下面好几道题都需要对这玩意处理一下。
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f const int mod=1e9+7; ll maxx=(1ll<<32)-1; string jin="0123456789abcdefghijklmnopqrstuvwxyz0"; map<char,int>m; bool vis[40]; ll poww(int ba,int times) { ll res=1; for(int i=0;i<times;i++){ res*=ba; } return res; } int main() { fast; //cout<<maxx<<'\n'; int t; cin>>t; string s; getline(cin,s); for(char i='0';i<='9';i++) m[i]=i-'0'; for(char i='a';i<='z';i++) m[i]=i-'a'+10; while(t--){ getline(cin,s); string ans=""; mem(vis,0); for(int i=0;i<s.length();i++){ if(s[i]>='0'&&s[i]<='9') vis[s[i]-'1'+1]=1; else if(s[i]>='a'&&s[i]<='z') vis[s[i]-'a'+10]=1; } char opt,optpos,equpos; for(int i=0;i<s.length();i++){ if(s[i]=='+'||s[i]=='-'||s[i]=='*'||s[i]=='/') { opt=s[i]; optpos=i; break; } } for(int i=0;i<s.length();i++){ if(s[i]=='=') { equpos=i; break; } } int n=s.length(); int minn=1; for(int i=1;i<37;i++) if(vis[i]) minn=i; // cout<<minn<<'\n'; bool flag=true,flag2=true; for(int i=0;i<n;i++) { if(s[i]=='0'){ flag2=false; break; } } if(minn==0){ cout<<"23456789abcdefghijklmnopqrstuvwxyz"; continue; } if(minn==1&&flag2){ int a=optpos-2+1,b=equpos-2-(optpos+2)+1,c=n-1-(equpos+2)+1; // cout<<a<<' '<<b<<' '<<c<<'\n'; // bool flag=true; if(opt=='+'&&(a+b)!=c) flag=false; if(opt=='-'&&(a-b)!=c) flag=false; if(opt=='*'&&(a*b)!=c) flag=false; if(opt=='/'&&(b*c)!=a) flag=false; // if(!flag) cout<<"invalid\n"; // if(flag) cout<<"1\n"; if(flag) ans+="1"; } if(opt=='+'||opt=='-'){ // ll a=0,b=0,c=0; for(int i=minn+1;i<=36;i++){ ll a=0,b=0,c=0; for(int j=optpos-2;j>=0;j--){ a+=m[s[j]]*poww(i,optpos-2-j); } for(int j=equpos-2;j>=optpos+2;j--){ b+=m[s[j]]*poww(i,equpos-2-j); } for(int j=n-1;j>=equpos+2;j--){ c+=m[s[j]]*poww(i,n-1-j); } if(a>maxx||b>maxx||c>maxx) continue; // if(i==16) cout<<a<<' '<<b<<' '<<c<<"\n"; if((a+b)==c&&opt=='+') ans+=jin[i]; else if((a-b)==c&&opt=='-') ans+=jin[i]; } } else if(opt=='*'||opt=='/'){ // ll a=0,b=0,c=0; for(int i=minn+1;i<=36;i++){ ll a=0,b=0,c=0; for(int j=optpos-2;j>=0;j--){ a+=m[s[j]]*poww(i,optpos-2-j); } for(int j=equpos-2;j>=optpos+2;j--){ b+=m[s[j]]*poww(i,equpos-2-j); } for(int j=n-1;j>=equpos+2;j--){ c+=m[s[j]]*poww(i,n-1-j); } if(a>maxx||b>maxx||c>maxx) continue; //cout<<a<<' '<<b<<' '<<c<<'\n'; if((a*b)==c&&opt=='*') ans+=jin[i]; else if((b*c)==a&&opt=='/') ans+=jin[i]; } } // cout<<minn<<'\n'; if(ans=="") cout<<"invalid\n"; else cout<<ans<<'\n'; } return 0; }
有一个要注意的地方就是,如果它是一个多进制的数,也可能是1进制,这点需要注意,不然会WA。
还有,如果是多进制而且1进制的话,要把1放在前面输出,不然也会WA。(所以特判要在处理字符串之前进行)
Hack test:
1
1 * 1 = 1
签到题。
n组读入。
A和B打赌骰骰子,A每次需要付出1块钱来打这个赌。
这个赌是这样的:
B给A一个S面的骰子,给他骰Y次,其中必须要有X次骰到R点以上,就算B赢。B每次获胜可以获得W块钱。
如果B赢钱的期望大(严格大于1)则输出yes,否则输出no
1 ≤ N ≤ 10000, 1 ≤ R ≤ S ≤ 20, 1 ≤ X ≤ Y ≤ 10, 1 ≤ W ≤ 100.

因为x,y比较小,我们甚至可以暴力计算组合数。
代码:
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f const int mod=1e9+7; ll c(ll m,ll n){ ll res=1; for(int i=1;i<=m;i++) res*=i; for(int i=1;i<=n;i++) res/=i; for(int i=1;i<=(m-n);i++) res/=i; // cout<<res<<'\n'; return res; } int main() { int t; cin>>t; while(t--){ double r,s,x,y,w; cin>>r>>s>>x>>y>>w; double win=(s-r+1)/(double)s,lose=((r-1)/(double)s); // cout<<win<<' '<<lose<<'\n'; double winp=0,losep; for(int i=x;i<=y;i++){ double temp=1; for(int j=0;j<i;j++) temp*=win; for(int j=0;j<(y-i);j++) temp*=lose; temp*=c((int)y,(int)i); // cout<<temp<<'\n'; winp+=temp; } losep=1-winp; // cout<<winp<<' '<<losep<<'\n'; db ans=w*winp; // cout<<ans<<'\n'; if(ans>1) cout<<"yes\n"; else cout<<"no\n"; } return 0; }
这个题如果开long double 会WA,一个学弟因为这个白给了好几发,估计是被卡了精度
给你一组n个不重复的非零二维向量,现在可以选取其中任意个向量让他们的和为(0,0).
求这样的选取方法数量。
向量规模:x,y两分量各自的绝对值不超过10,n<=40,保证答案不超过1e10(明示开long long)
显然我们如果直接暴力枚举所有情况2^40是必然会TLE的。
我们考虑把它们分成两组,然后对于每一组分别处理可能得到的和的情况,再和另一组里面的元素进行对比即可。
复杂度(n·2n) ,这里的n<=20.
具体看代码吧:
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f const int mod=1e9+7; vector<pii>v1,v2; map<pii,int>m1,m2; int main() { fast; int n; cin>>n; for(int i=0;i<n;i++){ pii p; cin>>p.first>>p.second; if(i%2==0) v1.pb(p); else v2.pb(p);//如果用其他方法分组当然也完全ok } for(int i=1;i<(1<<v1.size());i++){ //枚举所有可能的情况 pii sum={0,0}; for(int j=0;j<v1.size();j++){ if((1<<j)&i){//选了第j个 sum.first+=v1[j].first; sum.second+=v1[j].second; } } m1[sum]++;//得到这样的和的方法+1 } for(int i=1;i<(1<<v2.size());i++){//同上 pii sum={0,0}; for(int j=0;j<v2.size();j++){ if((1<<j)&i){ sum.first+=v2[j].first; sum.second+=v2[j].second; } } m2[sum]++; } ll ans=0; for(auto u:m1){ pii p=u.first; if(m2.count({-p.first,-p.second})){ ans+=m1[{p.first,p.second}]*(ll)m2[{-p.first,-p.second}]; //这个应该是显然的吧 } } ans+=(m1[{0,0}]+m2[{0,0}]);//别忘了加上天然形成的(0,0) cout<<ans; return 0; }
当然你可以不用这里用的二进制枚举方法,用20层for也不是不可以(手动滑稽
下一个
签到题。输入一个字符串,如果26个字母全了就输出“pangram”,否则输出“missing”+缺少的字母。
那直接转化成小写跑一遍就完事了,注意getline。
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f const int mod=1e9+7; bool num[26]; vector<string>ans; int main() { //fast; int t; scanf("%d",&t); t++; while(t--){ string anss=""; mem(num,0); string s; getline(cin,s); int len=s.length(); for(int i=0;i<len;i++){ s[i]=tolower(s[i]); } for(int i=0;i<len;i++){ if(s[i]<='z'&&s[i]>='a') num[s[i]-'a']=true; } bool flag=true; for(int i=0;i<26;i++){ if(!num[i]) { flag=false; break; } } if(flag){ anss="pangram"; ans.pb(anss); continue; } anss+="missing "; for(int i=0;i<26;i++){ if(!num[i]) anss+=(char)(i+'a'); } ans.pb(anss); } for(int i=1;i<ans.size();i++){ cout<<ans[i]<<'\n'; } return 0; }
有n个人要逃学回宿舍,他们有一件隐形斗篷可以防止被老师抓到。隐形斗篷只能容纳两个人。
每个人回宿舍的时间记做a_i,每一趟运输所花费的时间等于比较慢的那个人所要花费的时间。
让你求把他们所有人运送回宿舍所需要的最短时间。(2<=n<=15, a_i<=5000)
举个例子,4个人,每个人所需要的时间是1,2,7,10,那我们最佳的做法就是:
(1)1和2先回去,时间+2;
(2)1回来,时间+1;
(3)7和10过去,时间+10;
(4)2回来,时间+2;
(5)1和2过去,时间+2;
总耗时为17,所以输出17.
小学数学题,我还WA了一发()
做法是这样的:
先对他们的所需时间排个序。然后让速度最快的两个人过去,保证中途运输的时间最小。
然后比较下面两种做法的时间:
(1)最快的和次快的过去,最快的回来,两个最慢的过去,次快的再回来。时间a[n-1]+a[0]+2*a[1].
(2)1回来,送最慢的过去,1再回来,把次慢的送过去,1再回来。时间a[n-1]+a[n-2]+2*a[0]
一直处理到还剩小于等于3个人的时候。
如果一个人,那就把它的用时加上(a[0]),两个人就是把他们里面较慢的人速度加上(a[1]),三个人就最快的两个人过去,最快的回来,再把最慢的接过去,用时(a[0]+a[1]+a[2])。
那这道题就写完了:
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f const int mod=1e9+7; int a[17]; int main() { fast; int n; cin>>n; for(int i=0;i<n;i++) cin>>a[i]; sort(a,a+n); int ans=0; while(n>3){ ans+=min(a[n-1]+2*a[1]+a[0],a[n-1]+a[n-2]+2*a[0]); a[n-1]=0; a[n-2]=0; n-=2; } if(n==1) ans+=a[0]; else if(n==2) ans+=a[1]; else ans+=a[2]+a[0]+a[1]; cout<<ans; return 0; }

感觉有点解释不清楚qwq,怕胡乱解释害了大家,各位观众老爷自己看下题面吧。
大概意思是把一个长度为L的字符串放进一个m*m的方格里面,再给它类似转置的操作一下,输出转置后的结果(转置方法看下题面吧)
直接模拟就行,也是个签到题:
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f const int mod=1e9+7; char a[101][101],tran[101][101]; int main() { fast; int t; cin>>t; while(t--){ mem(a,'*'); mem(tran,'*'); string s; cin>>s; int len=s.length(); int m=sqrt(len); if(m*m!=len) m++; for(int i=0;i<len;i++){ a[i/m][i%m]=s[i]; } for(int i=0;i<m;i++){ for(int j=0;j<m;j++){ tran[j][m-1-i]=a[i][j]; } } for(int i=0;i<m;i++){ for(int j=0;j<m;j++){ if(tran[i][j]!='*') cout<<(char)tran[i][j]; } } cout<<'\n'; } return 0; }
输入n个字符串,如果是“Simon says”打头的就把这句话删除“Simon says”以后再输出。
纯签到题,如果不知道string.substr用法的同学们可以借这个题实践一下(还是有个getline的问题,注意一下)
代码:
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f const int mod=1e9+7; vector<string>ans; int main() { fast; int t; cin>>t; t++; while(t--){ string s; getline(cin,s); if(s=="") continue; if(s.substr(0,10)=="Simon says") ans.pb(s.substr(10)); } for(int i=0;i<ans.size();i++) cout<<ans[i]<<'\n'; return 0; }
有n块碎片,每块碎片上有一条路线。
现在给你这些信息,再告诉你一个起点和终点,问这两个点之间有没有路。有的话输出路径,没有的话输出“no route found”。
这题有意思的地方是起点和终点啥的都是地铁站的名字,所以你需要用string来存。我这里用的方法是用两个map来对应信息。
下面就是dijkstra可以解决的问题了,单源最短路,完事。数据规模2<=n<=32.
因为每个点后面可能会有n-1个不同站点,而且看样例可以发现可能不是n个站点中的其中一个。
所以这里数组开到1050,因为32*32=1024.
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f const int mod=1e9+7; map<string,int>m; map<int,string>mm; vector<string>v; bool vis[1050]; int dis[1050],pre[1050]; int g[1050][1050]; int n,cnt=0; void dij(int st){ dis[st]=0; vis[st]=true; for(int i=0;i<cnt;i++){ int minn=inf,nxt=st; for(int j=0;j<cnt;j++){ if(!vis[j]&&minn>dis[j]){ minn=dis[j]; nxt=j; } } vis[nxt]=1; for(int j=0;j<cnt;j++){ if(!vis[j]){ if(dis[j]>dis[nxt]+g[nxt][j]){ dis[j]=dis[nxt]+g[nxt][j]; pre[j]=nxt; } } } } } void path(int end){ if(pre[end]!=-1){ path(pre[end]); cout<<mm[pre[end]]<<' '; } } int main() { fast; cin>>n; for(int i=0;i<1050;i++) for(int j=0;j<1050;j++){ if(i==j) g[i][j]=0; else g[i][j]=inf; } mem(dis,0x3f); mem(pre,-1); string s; getline(cin,s); for(int i=0;i<n;i++){ v.clear(); getline(cin,s); string temp=""; for(int j=0;j<s.length();j++){ if(s[j]==' '){ v.pb(temp); temp=""; continue; } else temp+=s[j]; } v.pb(temp); for(int j=0;j<v.size();j++){ if(m.count(v[j])==0){ m[v[j]]=cnt; mm[cnt]=v[j]; cnt++; } } for(int j=1;j<v.size();j++){ g[m[v[0]]][m[v[j]]]=1; g[m[v[j]]][m[v[0]]]=1; } } string stt,edd; cin>>stt>>edd; if(!m.count(stt)||!m.count(edd)){ cout<<"no route found\n"; return 0; } dij(m[stt]); if(dis[m[edd]]==inf){ cout<<"no route found\n"; return 0; } path(m[edd]); cout<<mm[m[edd]]; return 0; }
当然这题据说直接bfs也是可以做的。
那这个题解就到这里了
看有俱乐部大佬过了K题,如果我明天看这个有思路会写(或者参考别人题解能写出来的话),再来把这个补掉qwq

浙公网安备 33010602011771号