HPU暑期集训积分赛2

时间:2022-12-20 22:49:12

A. 再战斐波那契

单点时限: 1.0 sec

内存限制: 512 MB

小z 学会了斐波那契和 gcd 后,老师又给他出了个难题,求第N个和第M个斐波那契数的最大公约数,这可难倒了小z ,不过在小z 的再三请求下,老师又告诉他了个条件,gcd(N,M)∈[1,90]。
可是,笨拙的小z 还是不会,于是请求你帮他解答这个问题。

HPU暑期集训积分赛2

输入格式

输入包括 T 组,T∈[1,10]. 
接下来 T 行,每行两个整数 N,M, 表示斐波那契的第 N 项和第 M 项,(N,M∈[1,1018]).

输出格式

输出包含 T 行,每行输出一个整数.

样例

input

3

1 2

2 3

3 4

output

1

1

1

 思路:

 点击见详细推导

代码:

HPU暑期集训积分赛2HPU暑期集训积分赛2
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll arr[110];
void fib(){
    for(int i=2;i<=100;i++){
        arr[i]=arr[i-1]+arr[i-2];
    }
    return ;
}
int main(){
    ll n,a,b;
    arr[0]=0;
    arr[1]=1;
    fib();
    cin>>n;
    while(n--){
        cin>>a>>b;
        cout<<arr[__gcd(a,b)]<<endl;
    }
    return 0;
}
View Code

 

B. 恐怖的怪物

单点时限: 5.0 sec

内存限制: 512 MB

一天早上,Dicer一觉醒来,发现自己来到了MineCraft的世界里面,身为MineCraft游戏爱好者的他欣喜不已,于是他在地下挖了一片长方体的空间作为秘密基地,可是他发现光照亮度小于等于7时,会有恐怖的怪物出现,并且他通过查阅资料发现光源方块产生光照每一米(方格)衰减1光照等级。

此规律在坐标轴的3个方向上(东西、南北、上下)均成立。换句话来说,对角线方向的光照衰减依照“曼哈顿距离”(两个点在坐标系上的绝对轴距总和)计算。这意味着,假如地上插着一支火把(光照等级14),则在水平面上与火把相邻的4个方向的方格上光照等级均为13,而在水平面上与火把对角的4个方格上光照等级均为12(譬如,西北方格的光照等级为14-向西1-向北1级)。

上述这种衰减特性会在光源周围产生菱形的照明。该效果会在光源周围的光源扩散呈钻石状。如果被不透明方块阻挡,光照也可以沿着复杂而弯曲的路径扩散。

如下图所示,红色为光源(亮度等级为14,黑色为秘密物品,其余各个位置光照强度如图所示。

 

HPU暑期集训积分赛2

 

秘密基地为N∗M的空间,不考虑高度,初始地面光照强度为0。为了不生成恐怖的怪物,Dicer布置了一些光源,但他不知道是否仍会生成怪物,现在请你帮助Dicer判断。

注:光源及秘密物品均为不透明方块,且其上方均不会生成怪物。

输入格式

第一行是一个T。(1≤T≤100)
接下来有T组数据,每一组第一行是N,M,1≤N,M≤1000,接下来有N行,每行M个字符,代表秘密基地地面放置的方块,0代表空气,#代表秘密物品,Y代表萤石(光照等级为15)H代表火把(光照等级为14)F代表附魔台(光照等级为12)R代表激活的红石火把(光照等级为7)

输出格式

输出包含T行,每行如果仍会生成怪物,输出Yes,否则输出”No”。

样例

input

2

2 3

0Y0

00#

3 4

R00#

00R0

0R00

output

No

Yes

input

2

1 5

0Y0R0

2 4

Y#0R

0000

output

Yes

No

input

1

5 4

Y0F0

0000

0000

0000

0000

output

No

提示

本题数据量过大,建议使用scanf读入数据。

本题思路:

使用bfs将所有光源向周围扩散,因为数据过大且只需要判断每一个点是否大于7即可,所以可以将光源亮度减少7,在bfs判断该点是否大于0。当一个点已经被扩散到的时候那么如果再次扩散到并且亮度大于起始,那么便将这点再次加入队列。

代码:

 

HPU暑期集训积分赛2HPU暑期集训积分赛2
#include <bits/stdc++.h>
using namespace std;
char arr[1000+10][1000+10];
int vis[1000+10][1000+10];
int dy[4]={1,-1,0,0};
int dx[4]={0,0,1,-1};
int t,n,m;
struct aa{
    int x,y,d;
};
int bfs(int a,int b,int s){
    queue<aa>que;
    vis[a][b]=max(s,vis[a][b]);
    que.push({a,b,vis[a][b]});
    int x,y,d,xx,yy;
    while(que.size()){
        x=que.front().x;
        y=que.front().y;
        d=que.front().d;
        que.pop();
        for(int i=0;i<4;i++){
            xx=x+dx[i];
            yy=y+dy[i];
            if(xx>=0&&yy>=0&&xx<n&&yy<m&&d-1>vis[xx][yy]&&arr[xx][yy]=='0'){
                vis[xx][yy]=d-1;
                que.push({xx,yy,d-1});
            }
        }
    }
    return 0;
}

int main(){
    cin>>t;
    while(t--){
        int flag=0;
        memset(vis,0,sizeof(vis));
        cin>>n>>m;
        for(int i=0;i<n;i++){
            scanf("%s",&arr[i]);
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(arr[i][j]=='R'){
                    bfs(i,j,7-7);
                }
                else if(arr[i][j]=='H'){
                    bfs(i,j,14-7);
                }
                else if(arr[i][j]=='F'){
                    bfs(i,j,12-7);
                }
                else if(arr[i][j]=='Y'){
                    bfs(i,j,15-7);
                }
            }
        }

        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(vis[i][j]<=0&&arr[i][j]=='0'){
                    flag=1;
                    break;
                }
            }
            if(flag){
                break;
            }
        }
        if(flag){
            puts("Yes");
        }
        else{
            puts("No");
        }
    }
    return 0;
}
View Code

 

 

C. 连连看

单点时限: 3.0 sec

内存限制: 512 MB

众所周知,《连连看》是一个老少皆宜的游戏。 
《连连看》是由黄兴武创作的一款PC端益智类游戏,只要将相同的两张牌用三根以内的线段连在一起就可以消除,规则简单容易上手。

现在呢,Boctorio学长突然想玩连连看了,但不是单纯的玩游戏,他想自己出一局连连看。
由于Boctorio学长是一个蒟蒻,他不知道自己出的连连看是否符合能够通过多次操作将其全部消除,所以想要你帮他检查一下他出的连连看是否符合规则。

输入格式

第一行输入个T,表示T组数据1≤t≤100 
每组数据第一行两个数 n,m ,表示连连看棋盘的长和宽(1≤n,m≤100 
接下来 n 行,每行输入 个正整数aij,表示 m 个棋子 (1≤aij≤n∗m)。

每种棋子只会出现一对,因此数据保证只有一种有效结果。

输出格式

每组数据输出一行。 
如果棋盘符合规定,输出Yes”,否则,输出”No”(不包括引号)。

样例

input

3

2 2

1 2

2 1

3 4

1 6 2 3

4 5 3 1

4 2 6 5

4 4

1 2 3 6

8 4 7 8

5 6 5 7

1 2 3 4

output

No

No

Yes

 

本题思路:

我的想法是用一个vis数组记录每个点是否可达,然后将可达到点dfs看是否可以到达其对应点,dfs中如果转弯次数大于3或者已经找到对应点那么便return;但是实力有限,还是wa了。所以附上大佬代码。

 

HPU暑期集训积分赛2HPU暑期集训积分赛2
错误代码:
#include <bits/stdc++.h>
using namespace std;
int arr[110][110];
int vis[110][110];
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
int n,m,ii,jj,flag=0,ans=0;
void dfs(int x,int y,int a,int d,int turn){
    if(flag)
        return ;
    int xx,yy,f;
    for(int i=0;i<4;i++){
        if(flag) return ;
        xx=x+dx[i];
        yy=y+dy[i];
        if(d==i) ;
        else turn++;
        if(xx>=0&&yy>=0&&xx<n+2&&yy<m+2&&vis[xx][yy]==0&&(arr[xx][yy]==0||arr[xx][yy]==a)&&turn<4){
            if(arr[xx][yy]==a){
                flag=1;
                vis[xx][yy]=0;
                arr[xx][yy]=0;
                for(int k=0;k<4;k++){
                    vis[xx+dx[k]][yy+dy[k]]=0;
                }
                return ;
            }
            vis[xx][yy]=1;
            dfs(xx,yy,a,i,turn);
            if(d!=i) turn--;
            vis[xx][yy]=0;
        }
        else{
            if(d!=i) turn--;
        }
    }
    return ;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        memset(vis,0,sizeof(vis));
        memset(arr,0,sizeof(arr));
        ans=0;
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&arr[i][j]);
                if(i!=1&&j!=1&&i!=n&&j!=m){
                    vis[i][j]=1;
                }
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(vis[i][j]==0&&arr[i][j]!=0){
                    flag=0;
                    vis[i][j]=1;
                    dfs(i,j,arr[i][j],-1,0);
                    if(flag){
                        arr[i][j]=0;
                        for(int k=0;k<4;k++){
                            vis[i+dx[k]][j+dy[k]]=0;
                        }
                    }
                    vis[i][j]=0;
                    if(flag){
                        i=0;
                        break;
                    }
                }
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(arr[i][j]){
                    ans=1;
                    break;
                }
            }
            if(ans){
                break;
            }
        }
        if(ans)
            puts("No");
        else
            puts("Yes");
    }
    return 0;
}

大佬代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N=105;
int debug,T,m,n,L[N][N],R[N][N],U[N][N],D[N][N],A[N][N];
vector<int>G[N*N];

void ers(int x,int y){
    A[x][y]=0;
    int l,u,r,d;
    for(l=y;l>=0&&!A[x][l];--l); ++l;
    for(r=y;r<=m+1&&!A[x][r];++r); --r;
    for(u=x;u>=0&&!A[u][y];--u); ++u;
    for(d=x;d<=n+1&&!A[d][y];++d); --d;
    for(int i=l;i<=r;++i)
        L[x][i]=i-l,R[x][i]=r-i;
    for(int i=u;i<=d;++i)
        U[i][y]=i-u,D[i][y]=d-i;
    if(r<m+1)L[x][r+1]=r-l+1;
    if(l>0)R[x][l-1]=r-l+1;
    if(u>0)D[u-1][y]=d-u+1;
    if(d<n+1)U[d+1][y]=d-u+1;
}

bool cers(int x1,int y1,int x2,int y2){
    for(int i=max(y1-L[x1][y1],y2-L[x2][y2]);i<=min(y1+R[x1][y1],y2+R[x2][y2]);++i){
        if((x1<x2&&x1+D[x1][i]>=x2-1)||(x1>x2&&x1-U[x1][i]<=x2+1)||x1==x2)
            return 1;
    }
    for(int i=max(x1-U[x1][y1],x2-U[x2][y2]);i<=min(x1+D[x1][y1],x2+D[x2][y2]);++i){
        if((y1<y2&&y1+R[i][y1]>=y2-1)||(y1>y2&&y1-L[i][y1]<=y2+1)||y1==y2)
            return 1;
    }
    return 0;
}

int main(){

    std::ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    cin>>T;
    for(int P=1;P<=T;++P){
        for(int i=0;i<N*N;++i)
            G[i].clear();
        memset(A,0,sizeof(L));
        memset(L,0,sizeof(L));
        memset(R,0,sizeof(L));
        memset(U,0,sizeof(L));
        memset(D,0,sizeof(L));
        cin>>n>>m;
        map<int,int>M;
        for(int i=1;i<=n;++i){
            L[i][1]=1,R[i][m]=1;
            U[i][0]=U[i][m+1]=i;
            D[i][0]=D[i][m+1]=n+1-i;
        }
        for(int i=1;i<=m;++i){
            U[1][i]=1,D[n][i]=1;
            L[0][i]=L[n+1][i]=i;
            R[0][i]=R[n+1][i]=m+1-i;
        }
        int p=0;
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                cin>>A[i][j];
                G[A[i][j]].push_back(i<<8|j);
            }
        }
        int cnt=n*m>>1;
        while(cnt){
            int flg=0;
            for(int i=0;i<N*N;++i){
                if(!G[i].size())continue;
                if(G[i].size()&1){
                    flg=0;
                    break;
                }
                assert(G[i].size()==2);
                if(cers(G[i][0]>>8,G[i][0]&0xff,G[i][1]>>8,G[i][1]&0xff))
                    ers(G[i][0]>>8,G[i][0]&0xff),
                    ers(G[i][1]>>8,G[i][1]&0xff),
                    flg=1,--cnt,G[i].clear();
            }
            if(!flg)break;
        }
        cout<<(cnt?"No":"Yes")<<endl;
    }
}
View Code

 

 

D. Points in rectangle

单点时限: 2.0 sec

内存限制: 512 MB

在二维平面中有一个矩形,它的四个坐标点分别为(0,a),(a,0),(n,n−a),(n−a,n)。你现在有m个点,现在你想知道有多少个点是在这个矩形内的(边上的也算)

输入格式

第一行输入n,a(1≤a<n≤103)。
第二行一个正整数m(1≤m≤103),代表你拥有的点的个数,接下来m行,每行一个点的坐标xi,yi(1≤xi,yi≤103)。

输出格式

第一行输出在矩形内的点的个数,然后输出在矩形内点的坐标,横坐标大的优先,如果横坐标相同,则纵坐标大的优先。如果没有,输出−1。

样例

input

6 1

5

1 2

1 3

2 3

3 4

4 5

output

4

4 5

3 4

2 3

1 2

 本题思路:

找到矩形的范围即可。

 

E. Numbers of interval

单点时限: 2.0 sec

内存限制: 512 MB

现在有一个数组,请计算有多少的区间[l,r](l≤r)满足 ∑ri=lai≥k.

输入格式

第一行输入n,k(1≤n,k≤106).
接下来输入n个数,第i个数为ai(1≤ai≤103).

输出格式

输出满足条件的区间个数

样例

input

3 5

2 3 5

output

4

 本题思路:

从数组首部开始,将数组元素按次序相加,如果大于给定值即可以算包含这些数的区间,然后删掉首元素,再次判断循环,直到数组结束。运气好没有超时,学长的前缀和加上二分非常值得学习。

代码:

HPU暑期集训积分赛2HPU暑期集训积分赛2
#include <bits/stdc++.h>
using namespace std;
int arr[1000000];
int main(){
    int n,k,q=0;
    long long sum=0,num1=0;
    cin>>n>>k;
    for(int i=0;i<n;i++){
        cin>>arr[i];
    }
    for(int i=0;i<n;i++){
        sum+=arr[i];
        while(sum>=k){
            num1+=(n-i);
            sum=sum-arr[q];
            q++;
        }
    }
    cout<<num1<<endl;
    return 0;
}
View Code

 

F. 剪纸

单点时限: 1.0 sec

内存限制: 512 MB

 签到题,略。

 

G. Fake hpuoj predictor

单点时限: 2.0 sec

内存限制: 512 MB

总所周知,HPU(Harmonious and Peaceful University) Online Judge具有一个强大的的rating(积分)系统,它采用的是国际上权威的ELO等级分制度(ELO Rating System)LOL,守望先锋,codeforces,topcoder等知名游戏的排行均是采用此制度。

 HPU暑期集训积分赛2

RA代表上轮比赛结束后的积分。
K为积分系数,对于不同等级的选手的K是不同的。
SA代表比赛实际总得分,对于每局比赛来说,每赢一个人就会加1,输了不扣分。
EAi代表A与第i个选手比赛获胜的期望。

HPU暑期集训积分赛2

现在他打完了这m场比赛后他迫切的想知道自己的rating变为了多少(因为管理员太懒了,已经鸽了m场的rating计算了),现在他想让你帮他写一个预测器来预测一下。

输入格式

单组输入,第一行输入一个m(1≤m≤100),代表codancer参加的比赛的数量。
接下来对于每场比赛:
第一行输入一个整数n代表有n(1≤n≤100)个人参加的比赛。
接下来n行每行输入一个字符串和数字,代表参赛选手的用户名和他的ratingcodancer即为他自己的用户名(用户名长度不超过20),假如输入的名字为codancer,则不用输入数字(其他参赛选手的rating是不会更新的,因为管理员太懒了)

输出格式

输出codancer最终的rating,向上取整。

样例

input

3

5

tourist 2000

capryang 1900

boctorio 1800

dicer 1800

codancer

2

codancer

rookie 200

2

wzy 1500

codancer

output

12

提示

每计算完一场都需要向上取整,建议参与运算的变量都使用double

本题思路:

解题关键在于能否理解公式,公式理解了基本上就解决了。函数ceil()是小数向上取整。

代码:

 

HPU暑期集训积分赛2HPU暑期集训积分赛2
#include <bits/stdc++.h>
using namespace std;
double K(double rating){
    if(rating<1350) return 15.0;
    else if(rating<1500) return 20.0;
    else if(rating<1600) return 25.0;
    else if(rating<1700) return 30.0;
    else if(rating<1800) return 35.0;
    else return 50.0;
}
double qi(double a,double b){
    return 1.0/(1.0+pow(10,(b-a)/400.0));
}
int main(){
    int n,a,m;
    string s;
    double fen,rate=0.0;
    double qi_wang=0.0;
    cin>>n;
    while(n--){
        qi_wang=0.0;
        cin>>a;
        for(int i=1;i<=a;i++){
            cin>>s;
            if(s=="codancer"){
                m=a-i;
            }
            else{
                cin>>fen;
                qi_wang=qi_wang+qi(rate,fen);
            }
        }
        rate=rate+K(rate)*(m-qi_wang);
            rate=ceil(rate);
    }
    cout<<rate<<endl;
    return 0;
}
View Code

 

 

H. 花花与三猫Catlive

单点时限: 1.0 sec

内存限制: 512 MB

“大佬”中分和“呆B”李白正在玩一个游戏,游戏规则是这样的:
1. 游戏刚开始的时候,中分和李白相距L步,相对而望。
2. 老父亲和老母亲手中各有一个M个面的均匀骰子。(也就是说可以随机生成[1,m]内的任意一个数字,且概率均等)
3. 在每个回合开始的时候,老父亲和老母亲都会掷一下手中的骰子。
4. 当老父亲的骰子掷到1的时候,中分可以向李白走一步。
5. 当老母亲的骰子掷到m的时候,李白可以向中分走一步。
6. 当中分和李白相遇的时候,游戏结束。

可是老父亲和老母亲刚刚拍完新节目,他们太累了,不想做这个游戏,但是他们还很想知道,这个游戏平均需要多少次才能结束。聪明的你,能告诉他们吗?

结果是一个实数s,可以证明s能被表示成一个分数 qp,请输出q⋅p−1,其中q−1表示q在模109+7意义下的逆元。

输入格式

第一行是一个正整数 T(1≤T≤1000),表示测试样例的组数。
接下来T行,每行两个正整数L,M(1≤L,M≤1000),含义如题面描述。

输出格式

输出包括T行,每行一个答案。

样例

input

2

1 2

2 1

output

1

1

提示

2在模109+7意义下的逆元是500000004

 解题思路:

没有读懂题,懂了之后很简单。

代码:

HPU暑期集训积分赛2HPU暑期集训积分赛2
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
typedef long long ll;
int main(){
    ll T;
    scanf("%d", &T);
    ll L, M;
    while(T--){
        cin>>L>>M;
        cout<<L * M * 500000004 % MOD<<endl;
    }
}
View Code

 

I. Same String

单点时限: 2.0 sec

内存限制: 512 MB

有两个只由小写字母组成的长度为n的字符串s1,s2和m组字母对应关系,每一组关系由两个字母c1和c2组成,代表c1可以直接变成c2,你需要判断s1是否可以通过这m组关系转换为s2。

输入格式

第一行输入一个n(1≤n≤100),代表字符串的长度。
第二行和第三行输入两个字符串s1,s2。
第四行输入一个m(1≤m≤325),代表有m组关系。
接下来m行,第i行两个字符ui,vi,代表ui可以直接变为vi。

输出格式

如果s1可以通过这些m组关系转化变为s2,输出YES”,否则输出”NO”。

样例

input

6

aabbcc

cdbcad

4

a c

c a

a d

b c

output

YES

提示

可以转换多次,比如a可以转换为b,而b可以转换为c,则a可以转换为c
样例一:aabbcc->cabbcc->cdbbcc->cdbccc->cdbcac->cdbcaa->cdbcad

 本题思路:

用bfs,warshall算法都可以,但我都不会,所以写了个垃圾代码,做这题的时候感觉要爆零了。

代码:

HPU暑期集训积分赛2HPU暑期集训积分赛2
#include <bits/stdc++.h>
using namespace std;
typedef pair<char,char> P;
vector<P>arr;
int check(char c1,char c2){
    if(c1==c2){
        return 0;
    }
    for(int i=0;i<arr.size();i++){
        if(arr[i].first==c1&&arr[i].second==c2){
            return 0;
        }
    }
    return 1;
}

int main(){
    map<P,int>mp;
    int n,m,flag=0;
    string s1,s2;
    char c1,c2;
    cin>>n>>s1>>s2>>m;
    for(int i=0;i<m;i++){
        cin>>c1>>c2;
        arr.push_back({c1,c2});
        mp[{c1,c2}]++;
    }
    for(int i=0;i<arr.size();i++){
        for(int j=0;j<arr.size();j++){
            if(arr[i].second==arr[j].first&&i!=j){
                if(check(arr[i].first,arr[j].second)){
                    arr.push_back({arr[i].first,arr[j].second});
                    mp[{arr[i].first,arr[j].second}]++;
                }
            }
        }
    }
    for(int i=0;i<n;i++){
        if(s1[i]!=s2[i]){
            mp[{s1[i],s2[i]}]++;
            if(mp[{s1[i],s2[i]}]==1){
                flag=1;
                break;
            }
        }
    }
    if(flag){
        puts("NO");
    }
    else{
        puts("YES");
    }
}
View Code

 

这次积分赛自己的运气还挺好,更要加油努力