B、Fire-Fighting Hero
图论题-单源最短路径:添加一个顶点,连接各个救火团队所在的救火点,路径长度均设为 0,设该顶点为源,即变成了单源最短路径问题。使用两次Dijkstra算法可求出两个最短路径 的最大值。比较时将救火团队的乘以C进行比较可避免分数操作。
#include<iostream> #include<string.h> #include<algorithm> #include<math.h> #define INF 0x3f3f3f3f using namespace std; int vis[1005],way[1005][1005],dis[1005],a[1005]; int n,m; //vis[i]标记已经处理过的点,dis[j]记录起点s到点j最近的距离,way[][]表示路径关系 void init() { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(i==j) way[i][j]=0; else way[i][j]=INF; } } } void Dijkstra(int s) { int mn; for (int i = 1; i <= n; i++)//初始化 { vis[i] = 0; dis[i] = way[s][i]; } vis[s] = 1; for (int i = 1; i <= n; i++) { int u; mn = INF; u = -1; for (int j = 1; j <= n; j++)//找出离初始起点s直接距离最近的点,记录下标和最近距离 { if (vis[j] == 0 && dis[j] < mn) { u = j; mn = dis[j]; } } if (u == -1)//如果没有找到 break; vis[u] = 1; for (int j = 1; j <= n; j++)//更新 { if (vis[j] == 0) { if (dis[u] + way[u][j] < dis[j]) dis[j] = dis[u] + way[u][j]; } } } } int main() { int t; scanf("%d",&t); while(t--) { int s,k,c; scanf("%d%d%d%d%d",&n,&m,&s,&k,&c);//n是起火点的个数,m是边数,s是消防英雄所在位置,k个消防队,c是系数 for(int i=0;i<k;i++) scanf("%d",&a[i]); init(); for(int i=0;i<m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); if(way[x][y]>z) { way[x][y]=z; way[y][x]=z; } } Dijkstra(s); int ans1=0; for(int i=1;i<=n;i++)//消防英雄到各个起火点距离的最大值 ans1=max(ans1,dis[i]); for(int i=0;i<k;i++)//把n个消防队连接起来,变成一个点 { for(int j=0;j<k;j++) way[a[i]][a[j]]=0; } Dijkstra(a[0]); int ans2=0; for(int i=1;i<=n;i++)//遍历消防队到各个起火点距离的最大值 ans2=max(ans2,dis[i]); if(ans1>ans2*c) printf("%d ",ans2); else printf("%d ",ans1); } return 0; }
E、Magic Master
题意:有n张连续的牌,一次操作m张牌,q次询问,每次询问数字为x的牌在洗牌之前的位置。
操作是:先把n张牌洗牌之后放在桌面上,每次从桌面拿一张牌到手中,拿完一张牌之后,把桌面上面的m张牌一次放到最底下,重复操作,直到拿完桌面上的所有牌;
最后手上的牌的顺序是依次递减的
题解:用队列逆向模拟还原即可:从第n张牌开始,把第n张牌从手里放到桌面之后,把最底下的牌放到最顶上,然后把第n-1张牌放到桌面,再把最底下的牌放到最顶上,直到手里没有牌
#include<iostream> #include<queue> using namespace std; int n,m,t,k; int b[40000005]; int q[40000005]; int main() { cin>>t; while(t--) { cin>>n>>m>>k; queue<int>p; for(int i=0;i<k;i++) cin>>q[i]; for(int i=n;i>=1;i--) { if(!p.empty()) { for(int j=1;j<=m;j++) { int top=p.front(); p.pop(); p.push(top); } } p.push(i); } for(int i=n;i>=1;i--) { int now=p.front(); b[i]=now; p.pop(); } for(int i=0;i<k;i++) cout<<b[q[i]]<<endl; } return 0; }
H、The Nth Item
题意:定义一个函数F,输入q(代表q次询问),n(代表给定的第一个值),q次询问只输出最后的结果并对998244353取模
假设第i次询问的结果是F[ ni ] = temp,那么下一次的n[i+1]=n[i]^(temp*temp);
输出q次询问的异或和ans=ans^temp;
#include <iostream> #include<string.h> #include<stdio.h> #include<map> #define ll long long using namespace std; const ll mod = 998244353; map<ll,ll>mp; struct mat { ll m[2][2]; mat() { memset(m, 0, sizeof(m)); } }; mat mul(mat &A, mat &B) { mat C; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { C.m[i][j] = (C.m[i][j] + A.m[i][k] * B.m[k][j]) % mod; } } } return C; } mat pow(mat A, ll n) { mat B; B.m[0][0] = B.m[0][1] = 1; while (n) { if (n & 1) B = mul(A, B); A = mul(A, A); n >>= 1; } return B; } int main() { ll n,ans=0,q,temp; scanf("%lld%lld",&q,&n); while (q--) { if(mp[n])//如果F[n]已经计算过就直接用 ans=ans^mp[n]; else { if(n<2) temp=n; else { mat A; A.m[0][0] = 3; A.m[0][1] = 2; A.m[1][0] = 1; mat B = pow(A, n); temp=B.m[1][0]; } ans=ans^temp; mp[n]=temp; } n=n^(temp*temp); } printf("%lld ", ans); return 0; }