训练联盟训练赛之 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;
}
View Code

有一个要注意的地方就是,如果它是一个多进制的数,也可能是1进制,这点需要注意,不然会WA。

还有,如果是多进制而且1进制的话,要把1放在前面输出,不然也会WA。(所以特判要在处理字符串之前进行)

Hack test:

1

1 * 1 = 1

 

Problem B Bobby’s Bet

签到题。

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;
}
View Code

这个题如果开long double 会WA,一个学弟因为这个白给了好几发,估计是被卡了精度

 

Problem D Circuit Counting

给你一组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;
}
View Code

 

当然你可以不用这里用的二进制枚举方法,用20层for也不是不可以(手动滑稽

下一个

 

Problem F Quick Brown Fox

签到题。输入一个字符串,如果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;
}
View Code

 

Problem G Safe Passage

有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;
}
View Code

 

Problem H Secret Message

 

感觉有点解释不清楚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;
}
View Code

 

Problem I Simon Says

输入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;
}
View Code

 

Problem J Torn To Pieces

有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;
}
View Code

当然这题据说直接bfs也是可以做的。

 

那这个题解就到这里了

看有俱乐部大佬过了K题,如果我明天看这个有思路会写(或者参考别人题解能写出来的话),再来把这个补掉qwq

posted @ 2020-04-19 22:52  Doomfist  阅读(249)  评论(0)    收藏  举报