[关闭]
@morehigh 2017-04-14T15:33:32.000000Z 字数 5491 阅读 932

CQUPT 训练题 2017/4/3

未分类


A - Country Roads LightOJ - 1002
题意:

  1. T组测试样例,每组测试样例包含N,M表示有n个点,m条路径,每条路径从uv权值为w,求出所有点到目的地点t的路径权值最小(此权值表示这条路径上最大的两点之间的权值)。

解题思路:

  1. dijsktra算法的变形,将d[i]保存目的地到第i点路径的最小权值,map[u][j]表示uj的权值,每次比较最短路径时,
  2. tmp=max(d[u],map[u][j]), d[j]=min(d[j],tmp);需要注意的是,输入的时候可能有重复的边去最小的输入。

代码:

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <algorithm>
  5. #define LL long long
  6. #define inf 0x3f3f3f3f
  7. using namespace std;
  8. int d[600],vis[600],map[600][600];
  9. int n,m;
  10. void dijkstra(int s)
  11. {
  12. for(int i=0;i<n;i++)
  13. d[i]=inf;
  14. d[s]=0;
  15. vis[s]=1;
  16. for(int i=0;i<n-1;i++)
  17. {
  18. int u=s,mm=inf;
  19. for(int j=0;j<n;j++)
  20. {
  21. if(!vis[j]&&d[j]<mm)
  22. {
  23. u=j;
  24. mm=d[j];
  25. }
  26. }
  27. vis[u]=1;
  28. for(int j=0;j<n;j++)
  29. {
  30. if(!vis[j]&&map[u][j]!=inf)
  31. {
  32. int tmp=max(d[u],map[u][j]);
  33. d[j]=min(d[j],tmp);
  34. }
  35. }
  36. }
  37. }
  38. int main()
  39. {
  40. int t,cas=1;
  41. scanf("%d",&t);
  42. while(t--)
  43. {
  44. scanf("%d%d",&n,&m);
  45. for(int i=0;i<n;i++)
  46. {
  47. for(int j=0;j<n;j++)
  48. {
  49. if(i==j)
  50. map[i][j]=0;
  51. else
  52. map[i][j]=inf;
  53. }
  54. }
  55. int u,v,w;
  56. for(int i=0;i<m;i++)
  57. {
  58. scanf("%d%d%d",&u,&v,&w);
  59. if(map[u][v] > w)
  60. {
  61. map[u][v] = w;
  62. map[v][u] = w;
  63. }
  64. }
  65. int s;
  66. scanf("%d",&s);
  67. memset(vis,0,sizeof(vis));
  68. dijkstra(s);
  69. printf("Case %d:\n",cas++);
  70. for(int i=0;i<n;i++)
  71. {
  72. if(d[i]!=inf)
  73. printf("%d\n",d[i]);
  74. else
  75. printf("Impossible\n");
  76. }
  77. }
  78. return 0;
  79. }

C - Greatest Parent LightOJ - 1128
题意:

  1. 给出一棵树有N个节点,每个节点都有一个权值,给出q个查询,每个查询包含一个kv,k表示第k个节点,求出树根和k节点的路径上,权值大于等于v的离树根最近的节点

解题思路:

  1. dp[v][i] 表示节点y 它的2^i号祖先的路径上点权的最大值
  2. fa[v][i] 表示节点y 的第2^i 号祖先
  3. 状态转移方程:dp[y][i] = max(dp[y][i - 1], dp[fa[y][i-1]][i-1])

代码:

  1. #include <algorithm>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <cstdio>
  5. #include <cmath>
  6. #include <vector>
  7. using namespace std;
  8. const int maxn=100005;
  9. vector <int> g[maxn];
  10. int fa[maxn][18];
  11. int dp[maxn][18],val[maxn];
  12. void dfs(int x)
  13. {
  14. for(int i=0;i<g[x].size();i++)
  15. {
  16. int y=g[x][i];
  17. fa[y][0]=x;
  18. dp[y][0]=val[y];
  19. for(int j=1;j<18;j++) fa[y][j] = fa[fa[y][j-1]][j-1];
  20. for(int j=1;j<18;j++) dp[y][j] =max(dp[y][j-1],dp[fa[y][j-1]][j-1]);
  21. dfs(y);
  22. }
  23. }
  24. int solve(int x,int y)
  25. {
  26. for(int i=17;i>=0;i--)
  27. {
  28. if(dp[fa[x][i]][i]>=y) x=fa[x][i];
  29. }
  30. return x;
  31. }
  32. int main()
  33. {
  34. int t,cas=1,n,q,x;
  35. scanf("%d",&t);
  36. while(t--)
  37. {
  38. scanf("%d%d",&n,&q);
  39. for(int i=0;i<=n;i++)
  40. g[i].clear();
  41. val[0]=1;
  42. for(int i=0;i<18;i++)
  43. {
  44. dp[0][i]=val[0];
  45. fa[0][i]=0;
  46. }
  47. for(int i=1;i<n;i++)
  48. {
  49. scanf("%d%d",&x,&val[i]);
  50. g[x].push_back(i);
  51. }
  52. dfs(0);
  53. printf("Case %d:\n",cas++);
  54. int a,b;
  55. for(int i=0;i<q;i++)
  56. {
  57. scanf("%d%d",&a,&b);
  58. printf("%d\n",solve(a,b));
  59. }
  60. }
  61. return 0;
  62. }

D - Summing up Powers LightOJ - 1132
题意:

  1. (1^K + 2^K + 3^K + ... + N^K) % 2^32,给出NK求出结果

解题思路:

  1. 快速矩阵幂:
  2. f(x + 1) = f(x) + (x + 1) ^k,(x + 1)^k是一个二项展开式,这个展开式构造一个包含C(n, m)的矩阵,
  3. 当前矩阵:| f(x) x^k x^(k-1) x^(k-2) ... 1 |,目标矩阵:|fx+1} (x+1)^k (x+2)^k..... 1| 根据当前矩阵和目标矩阵求出状态转移矩阵。

代码:

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <cmath>
  5. #define LL long long
  6. const long long mod=(1LL<<32);
  7. struct Node
  8. {
  9. LL g[55][55];
  10. }res,ori;
  11. LL n,k,c[55][55];
  12. void init()
  13. {
  14. c[0][0]=1;
  15. for(int i=1;i<=50;i++)
  16. {
  17. c[i][0]=c[i][i]=1;
  18. for(int j=1;j<i;j++)
  19. {
  20. c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
  21. }
  22. }
  23. }
  24. Node mul(Node x,Node y)
  25. {
  26. Node temp;
  27. for(int i=0;i<k+2;i++)
  28. {
  29. for(int j=0;j<k+2;j++)
  30. {
  31. temp.g[i][j]=0;
  32. for(int l=0;l<k+2;l++)
  33. temp.g[i][j]=(temp.g[i][j]+(x.g[i][l]*y.g[l][j])%mod)%mod;
  34. }
  35. }
  36. return temp;
  37. }
  38. void sol(LL n)
  39. {
  40. while(n)
  41. {
  42. if(n&1)
  43. ori=mul(ori,res);
  44. n>>=1;
  45. res=mul(res,res);
  46. }
  47. }
  48. int main()
  49. {
  50. init();
  51. int t,cas=1;
  52. scanf("%d",&t);
  53. while(t--)
  54. {
  55. scanf("%lld%lld",&n,&k);
  56. memset(res.g,0,sizeof(res.g));
  57. memset(ori.g,0,sizeof(ori.g));
  58. res.g[0][0]=1;
  59. for(int i=0;i<k+1;i++)
  60. res.g[i+1][0]=c[k][i];
  61. for (int i = 1; i < k + 2; i++)
  62. for (int j = i, l = 0; j < k + 2; j++, l++)
  63. res.g[j][i] = c[k - i + 1][l];
  64. for(int i=0;i<k+2;i++)
  65. ori.g[0][i]=1;
  66. sol(n-1);
  67. printf("Case %d: %lld\n", cas++, ori.g[0][0]);
  68. }
  69. return 0;
  70. }

E - Dice (I) LightOJ - 1145
题意:

  1. N个骰子,每个骰子有K个面,分别从1-k进行标记,求N个骰子正面朝上之和为S的情况有多少种?

解题思路:

  1. 定义dp[i][j]为前i个骰子正面朝上和为j
  2. 状态转移方程:dp[i][j]=dp[i-1][j-1]+dp[i-1][j-2]....+dp[i-1][j-k];
  3. 这种写法的时间复杂度为:ON*S);
  4. 因此用滚动数组进行优化得到:
  5. dp[i][j]=dp[i-1][j-1]+dp[i-1][j-2]....+dp[i-1][j-k]
  6. dp[i][j-1]=dp[i-1][j-2]+....dp[i-1][j-k-1];
  7. dp[i][j]=dp[i][j-1]+dp[i-1][j-1]-dp[i-1][j-k-1];

代码:

  1. #include <algorithm>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <cstdio>
  5. #include <cmath>
  6. #include <vector>
  7. using namespace std;
  8. const int mod=100000007;
  9. long long dp[2][15010];
  10. int main()
  11. {
  12. int t,cas=1;
  13. scanf("%d",&t);
  14. while(t--)
  15. {
  16. int n,k,s;
  17. scanf("%d%d%d",&n,&k,&s);
  18. memset(dp,0,sizeof(dp));
  19. for(int i=1;i<=k;i++)
  20. dp[1][i]=1;
  21. for(int i=2;i<=n;i++)
  22. {
  23. for(int j=0;j<=s;j++)
  24. {
  25. if(j>k)
  26. dp[i%2][j]=((dp[i%2][j-1]+dp[(i+1)%2][j-1]-dp[(i+1)%2][j-k-1])+mod)%mod;
  27. else
  28. dp[i%2][j]=(dp[i%2][j-1]+dp[(i+1)%2][j-1])%mod;
  29. }
  30. // cout<<dp[i%2][0]<<endl;
  31. }
  32. printf("Case %d: %lld\n",cas++,dp[n%2][s]);
  33. }
  34. return 0;
  35. }

F - Diablo LightOJ - 1087
题意:

  1. 有一列数,有两种操作,第一种操作是‘a p’,向末尾添加一个数p,另一个操作是向‘c k’,找出第k个数,并将其从队列中删除,并输出第k个数

解题思路:

  1. 由于此题是在区间内进行查找和删除操作,id记录的是叶子节点数的值,如果第i个叶子节点不存在,id[i]=-1,num数组记录区间内有多少个id的值不为-1.if是‘c’操作,则将查询第k个数所在的位置,根据num数组来判定。

代码:

  1. #include <algorithm>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <cstdio>
  5. #include <cmath>
  6. #include <vector>
  7. using namespace std;
  8. const int maxn=200015;
  9. int ans;
  10. int num[maxn<<2],id[maxn<<2];
  11. char s[10];
  12. void build(int rt,int l,int r)
  13. {
  14. id[rt]=-1,num[rt]=0;
  15. if(l==r)
  16. return ;
  17. int m=(l+r)>>1;
  18. build(2*rt,l,m);
  19. build(2*rt+1,m+1,r);
  20. // num[rt]=num[rt<<1]+num[rt<<1|1];
  21. }
  22. void update(int k,int x,int l,int r,int rt)
  23. {
  24. if(l==r)
  25. {
  26. id[rt]=x;
  27. num[rt]=1;
  28. return ;
  29. }
  30. int m=(l+r)>>1;
  31. if(k<=m) update(k,x,l,m,rt<<1);
  32. else update(k,x,m+1,r,rt<<1|1);
  33. num[rt]=num[rt<<1]+num[rt<<1|1];
  34. }
  35. void query(int k,int l,int r,int rt)
  36. {
  37. // int ans;
  38. if(l==r)
  39. {
  40. ans=id[rt];
  41. id[rt]=-1;
  42. num[rt] = 0;
  43. return ;
  44. }
  45. int m=(l+r)>>1;
  46. if(k<=num[rt<<1]) query(k,l,m,rt<<1);
  47. else
  48. {
  49. k-=num[rt<<1];
  50. query(k,m+1,r,rt<<1|1);
  51. }
  52. num[rt]=num[rt<<1]+num[rt<<1|1];
  53. }
  54. int main()
  55. {
  56. int t,a,b,x,n,q;
  57. scanf("%d",&t);
  58. for(int cas=1;cas<=t;cas++)
  59. {
  60. scanf("%d%d",&n,&q);
  61. int nn=n+q;
  62. build(1,1,nn);
  63. // cout<<nn<<endl;
  64. for(int i=1;i<=n;i++)
  65. {
  66. scanf("%d",&x);
  67. update(i,x,1,nn,1);
  68. }
  69. printf("Case %d:\n",cas);
  70. for(int i=0;i<q;i++)
  71. {
  72. scanf("%s%d",s,&b);
  73. if(s[0]=='a')
  74. {
  75. n+=1;
  76. update(n,b,1,nn,1);
  77. }
  78. else
  79. {
  80. query(b,1,nn,1);
  81. if(ans==-1) printf("none\n");
  82. else
  83. printf("%d\n",ans);
  84. }
  85. }
  86. }
  87. return 0;
  88. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注