文章目录

  • 搜索
    • Curling 2.0(POJ 3009)
    • Meteor Shower(POJ 3669)
    • Smallest Difference(POJ 2718)
    • Hopscotch(POJ 3050)
  • 贪心
    • Cleaning Shifts(POJ 2376)
    • Radar Installation(POJ 1328)
    • Stall Reservations(POJ 3190)
    • Yogurt factory(POJ 2393)
    • Packets(POJ 1017)
    • Allowance(POJ 3040)
    • Stripies(POJ 1862)
    • Protecting the Flowers(POJ 3262)
  • 动态规划
      • Sumsets(POJ 2229)
    • Milking Time(POJ 3616)
  • 数据结构
    • Sunscreen(POJ 3614)
    • Moo University - Financial Aid(POJ 2010)
  • 并查集
      • Wireless Network
    • Find them, Catch them(POJ 1703)
  • 图论
    • Six Degrees of Cowvin Bacon(POJ 2139)
    • Wormholes(POJ 3259)
    • Silver Cow Party(POJ 3268)
    • Bad Cowtractors(POJ 2377)
    • Out of Hay(POJ 2395)
  • 数论
    • Dead Fraction(POJ 1930)
    • Prime Path(POJ 3126)
    • X-factor Chains(POJ 3421)
    • Semi-prime H-numbers(POJ 3292)
    • Raising Modulo Numbers(POJ 1995)
    • Pseudoprime numbers(POJ 3641)

搜索

Curling 2.0(POJ 3009)

题目链接:Curling 2.0
参考博文:POJ 3009 Curling 2.0(DFS + 模拟)

  • 题目大意:题意比较复杂,详见参考博文。
  • 思路:重要的是理解清楚题意。障碍物可以撞碎,球在遇见障碍物之前一定是直行。DFS回溯方法比较适合。具体思路参考博文。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;typedef long long LL;const int MAX = 25;
int maze[MAX][MAX];int dx[5] = {0, 1, 0, -1};
int dy[5] = {1, 0, -1, 0};
int ans;
int w, h;
int sx, sy;//判断坐标的状态
int check(int x, int y)
{if(maze[x][y]==0 || maze[x][y]==2) return 1;//可以移动else if(maze[x][y]==-1 || maze[x][y]==1) return 2;//不能移动else return 3;//到达目的地
}void dfs(int x, int y, int t)
{if(maze[x][y]==3 || t>10)//步数超出规定以及到达目的地均结束{if(ans>t) ans = t;}else{//四个方向遍历for(int i=0; i<4; i++){int tx = x, ty = y;if(check(tx+dx[i], ty+dy[i]) != 2)//只进行可以移动的状态操作{while(check(tx+dx[i], ty+dy[i])==1)//如果前方可以移动,一直向同一个方向移动,步数不更新tx += dx[i], ty += dy[i];if(maze[tx+dx[i]][ty+dy[i]]==1)//前方障碍物{//当前方是障碍物时,撞碎变成0,更新了地图maze[tx+dx[i]][ty+dy[i]] = 0;t++;dfs(tx, ty, t);//继续从障碍物前一个开始t--;maze[tx+dx[i]][ty+dy[i]] = 1;}else if(maze[tx+dx[i]][ty+dy[i]]==3)//到达目的地{t++;dfs(tx+dx[i], ty+dy[i], t);//直接到达目的地,结束t--;}}}}
}int main()
{while(scanf("%d%d", &w, &h)!=EOF && (w+h)){memset(maze, -1, sizeof(maze));ans = 1<<29;for(int i=1; i<=h; i++){for(int j=1; j<=w; j++){scanf("%d", &maze[i][j]);if(maze[i][j]==2)sx = i, sy = j;}}dfs(sx, sy, 0);if(ans>10)printf("-1\n");elseprintf("%d\n", ans);}return 0;
}
Meteor Shower(POJ 3669)

题目链接:Meteor Shower

  • 题目大意:给m个炸弹,炸弹在第t秒钟会在(x,y)处爆炸,爆炸会波及周围的四个点。求离(0,0)最近的没有被爆炸波及的点。
  • 思路:广搜。将安全区标记为-1,将爆炸区标记为该点最早爆炸的时间点,bfs遍历爆炸区,目标到达任意一个安全区。注意一个点可能爆炸多次,需要去最早爆炸的时间。
    代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;const int MAX = 500;struct Node
{int x, y, t;Node(int x=-1, int y=-1, int t=-1): x(x), y(y), t(t){}
};
queue<Node> q;
int M, X, Y, T;
int vis[MAX][MAX];//-1代表安全区,0代表已经遍历的地区,其余正值代表爆炸的时间
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};void bfs(Node s)
{while(!q.empty()) q.pop();q.push(s);if(vis[s.x][s.y]<0){printf("%d\n", &s.t);return;}if(vis[s.x][s.y]>0 && s.t<vis[s.x][s.y])vis[s.x][s.y] = 0;while(!q.empty()){Node u = q.front(); q.pop();for(int i=0; i<4; i++){int x = u.x+dx[i], y = u.y+dy[i], t = u.t+1;if(x>=0 && y>=0){if(vis[x][y]>0 && t<vis[x][y]){vis[x][y] = 0;q.push(Node(x, y, t));}else if(vis[x][y]<0){printf("%d\n", t);return;}}}}printf("-1\n");
}int main()
{scanf("%d", &M);memset(vis, -1, sizeof(vis));for(int i=0; i<M; i++){scanf("%d%d%d", &X, &Y, &T);//同一个点可能会爆炸多次,取最早爆炸的一次if(vis[X][Y]==-1)vis[X][Y] = T;else if(vis[X][Y]>T)vis[X][Y] = T;for(int j=0; j<4; j++){int x = X+dx[j], y = Y+dy[j];if(x>=0 && y>=0){if(vis[x][y]==-1)vis[x][y] = T;else if(vis[x][y]>T){vis[x][y] = T;}}}}bfs(Node(0, 0, 0));return 0;
}
Smallest Difference(POJ 2718)

题目链接:Smallest Difference

  • 题目大意:将k个数字分成两份,组成两个整数,求其最小的差值。
  • 思路:全排列的求解搜索。因为最多是0-9,所以如果全排列可知有428800中情况,不超时。注意nex_permutation(a, a+k)函数的使用(使用do循环)。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;int main()
{int T;string s;int a[15];int b, c;//b是大的那一方cin >> T;getchar();while(T--){getline(cin, s);int k = 0, cnt;for(int i=0; i<s.size(); i++){if(s[i]!=' '){a[k++] = s[i]-'0';}}//要排好第一个排列(升序)sort(a, a+k);int Min = 1<<29;do{b = c = 0;if(a[0]==0 || (a[k/2]==0 && k>2)) continue;//舍去这个排列for(int i=0; i<k/2; i++)b = b*10+a[i];for(int i=k/2; i<k; i++)c = c*10+a[i];cnt = abs(b-c);Min = min(Min, cnt);}while(next_permutation(a, a+k));//获取下一个排序cout << Min << endl;}return 0;
}
Hopscotch(POJ 3050)

题目链接:Hopscotch

  • 题目大意:牛可以在一个矩阵上任意一点上向前后左右四个方向跳格子,跳五次后得到含有六个数字的数字串,计算其组成的整数,求所得整数的可能的个数。
  • 思路:穷竭搜索。注意使用set结构体来存储枚举得到的结果,这样可以省去很多代码。遍历每一个点起始得到的整数,存入Set,最后得到set的大小。
    代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;const int MAX = 10;struct Node
{int x, y;Node(int x=-1, int y=-1): x(x), y(y){}
};int G[MAX][MAX];
int r[100000][MAX], n;
set<int> a;
Node b[MAX];
//右、左、下、上
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};int cnt = 0;void dfs(Node s, int step, int num)
{if(step==5){a.insert(num);return;}for(int i=0; i<4; i++){int x = dx[i]+s.x, y = dy[i]+s.y, t = step+1;if(x>=0 && x<5 && y>=0 && y<5){dfs(Node(x, y), t, num*10+G[x][y]);}}return;
}int main()
{n = 0;for(int i=0; i<5; i++){for(int j=0; j<5; j++){scanf("%d", &G[i][j]);}}for(int i=0; i<5; i++){for(int j=0; j<5; j++){dfs(Node(i, j), 0, G[i][j]);}}printf("%d\n", a.size());return 0;
}

贪心

Cleaning Shifts(POJ 2376)

题目链接:Cleaning Shifts

  • 题目大意:要求使用最少的区间覆盖一个大区间。
  • 思路:贪心。需要在可以首尾相接的条件下,保证得到的区间覆盖最远的距离。要注意首尾相接的条件可以不重合,但需要接上。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;const int MAX = 25005;
struct Node
{int s, e;bool operator < (const Node &A) const{return s<A.s;}
};
Node c[MAX];int main()
{int N, T;scanf("%d%d", &N, &T);for(int i=0; i<N; i++){scanf("%d%d", &c[i].s, &c[i].e);}sort(c, c+N);int start = 0, end = 0, cnt = 0, flag, ans = 1;for(int i=0; i<N; i++){flag = 0;//一定要注意是end+1if(c[i].s<=end+1 && c[i].e>cnt)//起始点可以接上,终点取满足起始点条件的最大值{cnt = c[i].e;}else if(c[i].s>end+1 && c[i].s<=cnt+1 && c[i].e>cnt)//有新的更新目标(即起始点接不上,但满足更新条件){end = cnt;ans++;i--;//注意这种情况需要重来一次}if(cnt==T)//满足终止条件{flag = 1;break;}}if(flag)printf("%d\n", ans);elseprintf("-1\n");return 0;
}
Radar Installation(POJ 1328)

题目链接:Radar Installation
参考博文:Radar Installation

  • 题目大意:在坐标系中,x轴上方为海洋(海洋中有多个岛屿,以点标记),x轴下方为陆地。需要在陆地上建立多个雷达站,使得雷达探测范围可以将所有岛屿覆盖,求需要建立的雷达站的最少数目。
  • 思路:比较好的贪心算法题目。将思想转换一下,计算探测到每一个岛屿的雷达站建设区域,在重合区域内建立雷达站即可探测到多个岛屿。需要记录区域的起始和终止,按起始点从小到大排序,然后不断更新重叠区域的终止点(可能变近),如果没有重叠区域则加一(起始点比重叠区域的终止点还大)。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;const int MAX = 1005;
struct Node
{double s, e;bool operator < (const Node &A) const{return s<A.s;}
};
Node a[MAX];
int n, d, x, y;int main()
{int Case = 1;while(scanf("%d%d", &n, &d)!=EOF){if(n==0 && d==0)break;int flag = 0;for(int i=0; i<n; i++){scanf("%d%d", &x, &y);if(y<=d){double bias = sqrt(d*d-y*y);a[i].s = x-bias, a[i].e = x+bias;}else{flag = 1;}}printf("Case %d: ", Case++);if(flag){printf("-1\n");continue;}//n不能是double类型,只能是intsort(a, a+n);int ans = 1;//注意使用double类型double start = a[0].s, end = a[0].e;for(int i=1; i<n; i++){if(a[i].s>end)//没有交集{ans++;//start = a[i].s;end = a[i].e;}else if(a[i].e<=end)//交集缩小{//start = a[i].s;end = a[i].e;}}printf("%d\n", ans);}return 0;
}
Stall Reservations(POJ 3190)

题目链接:Stall Reservations

  • 题目大意:每个牛在某个区间工作,需要占用一个牛棚,问至少需要准备多少牛棚,并给出一个分配方案。
  • 思路:贪心加上优先级队列的题。按照牛区间起始点从小到大排序并遍历,如果已经使用的牛棚的使用区间终止点比遍历的牛的区间起始点低,则该牛可以进入该牛棚工作,否则,新开一个牛棚。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;const int MAX = 50005;
int N, s, t, ans[MAX];
struct Node
{int s, e, id;//起始点,终止点,编号(用来当下标)Node(int s = -1, int e = -1, int id = -1): s(s), e(e), id(id){}
};
bool cmp1(Node A, Node B)
{return A.s<B.s;
}
struct cmp2
{bool operator () (Node &A, Node &B){return A.e>B.e;}
};Node cows[MAX];
Node stall[MAX];
priority_queue<Node, vector<Node>, cmp2> q;//用来筛选stall(选择终止点最小的)int main()
{while(scanf("%d", &N)!=EOF){for(int i=0; i<N; i++){scanf("%d%d", &cows[i].s, &cows[i].e);cows[i].id = i+1;}sort(cows, cows+N, cmp1);//起始点越靠前的越在前面int k = 1;ans[cows[0].id] = 1;//记录牛被分到了哪一个stall中q.push(Node(cows[0].s, cows[0].e, 1));for(int i=1; i<N; i++){//比较最小终止点是否小于下一个起始点if(cows[i].s<=q.top().e){k++;ans[cows[i].id] = k;q.push(Node(cows[i].s, cows[i].e, k));}else{Node t = q.top(); q.pop();t.e = cows[i].e;ans[cows[i].id] = t.id;q.push(t);}}printf("%d\n", k);for(int i=1; i<=N; i++){printf("%d\n", ans[i]);}while(!q.empty()) q.pop();}return 0;
}
Yogurt factory(POJ 2393)

题目链接:Yogurt factory

  • 题目大意:有一个奶酪工厂,给出这个工厂每天加工每个奶酪需要的价格,以及每天的需求量,另外,奶酪也可以存放在仓库里(竟然放不坏!),给出每个奶酪存放一天需要的成本,问,这些生产任务全部完成,最少的花费是多少。
  • 思路:这道题的贪心策略比较巧妙。可以直接从前向后更新每一天的奶酪生产价格(包括存储成本)。即:更新当前周的价格:min(当前周价格,上一周的价格+S),更新后则直接计算即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;typedef long long LL;
const int MAX = 10005;
int C[MAX], Y[MAX];
int N, S;
LL cost;int main()
{scanf("%d%d", &N, &S);for(int i=0; i<N; i++)scanf("%d%d", &C[i], &Y[i]);cost = 0;//更新当前周的价格:min(当前周价格,上一周的价格+S)for(int i=1; i<N; i++){C[i] = min(C[i], C[i-1]+S);}//计算值for(int i=0; i<N; i++){cost += C[i]*Y[i];}printf("%lld\n", cost);return 0;
}
Packets(POJ 1017)

题目链接:Packets
参考博文:POJ1017 Packets(贪心算法训练)

  • 题目大意: 公司共有底面面积为11、22、33、44、55、66,高度同为H的六种产品,现在需要用最少的箱子打包,箱子的底面面积为6*6,高度为H。
  • 思路:先装大的后装小的,空隙之间可能装小的,尽量填满空隙。因此需要从大到小以此模拟装入,具体参考博文。

代码:

#include <iostream>
#include <cstdio>
using namespace std;int a1, a2, a3, a4, a5, a6, s3, s2;int main()
{while(scanf("%d%d%d%d%d%d", &a1, &a2, &a3, &a4, &a5, &a6)!=EOF){if(a1+a2+a3+a4+a5+a6==0) break;int p = 0;p += a6/1;p += a5/1;a1 = max(a1-a5*11, 0);p += a4/1;if(a2<5*a4) a1 = max(a1-(5*a4-a2)*4, 0);a2 = max(a2-5*a4, 0);p += a3/4;s3 = a3%4;if(s3) p++;if(s3==1){if(a2<5) a1 = max(a1-(5-a2)*4-7, 0);else     a1 = max(a1-7, 0);a2 = max(a2-5, 0);}else if(s3==2){if(a2<3) a1 = max(a1-(3-a2)*4-6, 0);else     a1 = max(a1-6, 0);a2 = max(a2-3, 0);}else if(s3==3){if(a2<1) a1 = max(a1-(1-a2)*4-5, 0);else     a1 = max(a1-5, 0);a2 = max(a2-1, 0);}p += a2/9;s2 = a2%9;if(s2!=0) p++;if(s2) a1 = max(a1-(36-4*s2), 0);p += (a1+35)/36;printf("%d\n", p);}return 0;
}
Allowance(POJ 3040)

题目链接:Allowance
参考博文:Allowance(POJ-3040)

  • 题目大意:每周至少给牛 c 元,有 n 种硬币,已知每种硬币币值和数量,求最多坚持的周数。
  • 思路:将硬币从小到大排序,大于等于 c 元的硬币直接支付,然后将不能直接支付的先从大到小凑到接近 c ,再从小到大向上加,如果能达到就记录并开始下一轮,如果不能就退出。要注意的是,硬币无法拆分,因此只能多给不能少给。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;const int MAX = 1005;
const int INF = 1<<29;
struct Node
{int num, value;bool operator < (const Node &A) const{return value < A.value;}
};
int use[MAX];//记录硬币一周使用的个数
int n, c, cnt;int main()
{cnt = 0;scanf("%d%d", &n, &c);for(int i=1; i<=n; i++)scanf("%d%d", &coin[i].value, &coin[i].num);sort(coin+1, coin+1+n);//找出硬币币值大于c元的数目for(int i=1; i<n; i++){if(coin[i].value>=c){cnt += coin[i].num;//直接支付coin[i].num = 0;//清零}}while(true){bool flag = false;int sum = c;memset(use, 0, sizeof(use));//从大到小逼近cfor(int i=n; i>0; i--){if(coin[i].num>0){use[i] = min(sum/coin[i].value, coin[i].num);sum -= use[i]*coin[i].value;if(sum==0){flag = true;break;}}}//从小到大抵达或略微超过cif(sum>0){for(int i=1; i<=n; i++){if(coin[i].num-use[i]>0){while(use[i]<coin[i].num){sum -= coin[i].value;use[i]++;if(sum<=0){flag = true;break;}}}if(flag) break;}}if(!flag) break;//计算上面得出的方案最多可以进行几周int minn = INF;for(int i=1; i<=n; i++){if(use[i]>0)minn = min(minn, coin[i].num/use[i]);//记录所有硬币中最小的使用周数}for(int i=1; i<=n; i++){if(use[i]>0)//按照求出的使用周数,更新硬币的数目coin[i].num -= minn*use[i];}cnt += minn;}printf("%d\n", cnt);return 0;
}
Stripies(POJ 1862)

题目链接:Stripies

  • 题目大意:从N个数任取两个数按2×\times×sqrt(a*b)合成新数放回,求最后那个数的最小值。
  • 思路:贪心策略是使尽量使大的数多参与开方运算。每次取出最大和次大的数字运算,直至剩下一个数字。因为运算公式的原因,所以运算之后的结果一定是最大的数字,所以可以简化计算,直接排序之后遍历计算。

代码:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;const int MAX = 200;
bool cmp(int a, int b)
{return a>b;
}
int main()
{int n, m[MAX];cin >> n;for(int i=0; i<n; i++)cin >> m[i];sort(m, m+n, cmp);//从大到小排序double b = m[0];for(int i=1; i<n; i++){b = 2*sqrt(b*m[i]);}printf("%.3f", b);
}
Protecting the Flowers(POJ 3262)

题目链接:Protecting the Flowers

  • 题目大意:n头牛在吃花,每头牛牵走(避免吃花)要花费2*t秒(来回),每头牛吃花的速率是d,要你自己设计一个牵走牛的顺序,使被牛吃的花最少。
  • 思路:贪心算法。其实就是按d/t从大到小排序,然后依次牵走。重点是可能超时,需要在代码优化上下点功夫。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;typedef long long LL;
const int MAX = 100005;
struct Node
{int t, d;
};
bool cmp(Node a, Node b)
{double ap = a.d/(double)a.t;double bp = b.d/(double)b.t;return ap>bp;
}
Node C[MAX];
LL ans = 0, s = 0;int main()
{int N, T, D;while(scanf("%d", &N)!=EOF){for(int i=0; i<N; i++){scanf("%d%d", &C[i].t, &C[i].d);s += C[i].d;//先加上,后减去,降低了复杂度}sort(C, C+N, cmp);for(int i=0; i<N-1; i++){s -= C[i].d;ans += s*C[i].t*2;}printf("%lld\n", ans);}return 0;
}

动态规划

Sumsets(POJ 2229)

题目链接:Sumsets
参考博文:动态规划之划分数
DP:Sumsets(POJ 2229)

  • 题目大意:给一个数,将其进行分解,只能分解为2的幂次,最多能分解多少种。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;const int MAX = 1000001;
int dp[MAX];
int c;int main()
{int N;while(cin >> N){dp[0] = 1;c = 1;for(int i=1; i<=20 && c<=N; i++){for(int j=c; j<=N; j++){dp[j] = (dp[j]+dp[j-c])%1000000000;}c = c*2;//累乘,节省了时间}cout << dp[N] << endl;}return 0;
}
Milking Time(POJ 3616)

题目链接:Milking Time
参考博文:poj 3616 Milking Time —DP(带权重的区间动态规划)

  • 题目大意:题意就是有m头牛,每一只牛有一个产奶的时间段和一个奶量,Bessie可以去给每一头奶牛挤奶,但是每次给一个奶牛挤奶之后必须休息R分钟,问Bessie选择怎么挤奶可以使挤出的奶最多。
  • 思路:
    • 状态构造:dp[i]代表在前i个区间可以获得的最大奶量。
    • 终态:dp[M],M是牛的数量。
    • 状态转移方程:dp[i] = max(dp[i-1], dp[p[i]]+w[i](其中p[i]表示在第i个区间之前最近的一个与第i个区间不冲突的区间的下标,w[i]表示第i个区间的奶量。
    • 初始化:dp[0] = 0;

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;const int MAX = 1000001;
struct Node
{int s, e, v;bool operator < (const Node &A) const{return e<A.e;}
};
Node C[1005];
int dp[1005];
int p[1005];//上一个不重合可使用区间
int N, M, R, start, end, eff;
bool check(Node a, Node b)
{if(a.s>=b.e+R || b.s>=a.e+R) return 1;else return 0;
}
void Calculate_P(int M, int R)
{p[1] = 0;for(int i=M; i>1; i--){int k=i-1;while(k>0 && !check(C[k], C[i]))k--;p[i] = k;}
}
int main()
{while(scanf("%d%d%d", &N, &M, &R)!=EOF){for(int i=1; i<=M; i++){scanf("%d%d%d", &C[i].s, &C[i].e, &C[i].v);}sort(C+1, C+M+1);//按结束点排序Calculate_P(M, R);//计算每一个区间前面可以接着的最近区间下标memset(dp, 0, sizeof(dp));for(int i=1; i<=M; i++){dp[i] = max(dp[i-1], dp[p[i]]+C[i].v);//第i个区间是否使用}printf("%d\n", dp[M]);}return 0;
}

数据结构

Sunscreen(POJ 3614)

题目链接:Sunscreen
参考博文:Sunscreen (poj 3614 贪心+优先队列)

  • 题目大意:有c头牛晒太阳,每头牛都有一个能承受辐射的范围(min~max),现在有 l 种防晒霜,每种防晒霜都能将辐射值固定在spf,每种防晒霜都有一定的数量num。每头牛用最多一种防晒霜,问最多能满足多少头牛。
  • 思路:先将防晒霜按照spf从小到大排序,再将牛按照min从小到大排序,最后按序取出防晒霜,如果牛的min小于等于spf则入队列,将所有满足条件的牛入队列,然后找出其max最小的牛且max>=spf。循环以上步骤直到防晒霜用完或牛遍历完。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;const int MAX = 2505;
struct Sun
{int spf, num;
};
struct Cow
{int min, max;
};
struct cmp
{bool operator ()(Cow a, Cow b){return a.max>b.max;}
};
bool cmp1(Cow a, Cow b)
{return a.min<b.min;
}
bool cmp2(Sun a, Sun b)
{return a.spf<b.spf;
}
priority_queue<Cow, vector<Cow>, cmp> Q;
Cow Cow[MAX], p;
Sun S[MAX];int main()
{int C, L;cin >> C >> L;for(int i=0; i<C; i++)cin >> Cow[i].min >> Cow[i].max;for(int i=0; i<L; i++)cin >> S[i].spf >> S[i].num;sort(Cow, Cow+C, cmp1);Cow[C].min = 1<<29, Cow[C].max = 1<<29;//哨点sort(S, S+L, cmp2);int k = 0, ans = 0;for(int i=0; i<=C; i++){if(k==L) break;if(Cow[i].min<=S[k].spf)Q.push(Cow[i]);else if(!Q.empty())//符合该防晒霜的牛已经全部进队列{p = Q.top();Q.pop();while(p.max<S[k].spf && !Q.empty()){p = Q.top(); Q.pop();}if(p.max>=S[k].spf){S[k].num--;if(S[k].num==0)k++;ans++;}i--;}else if(Q.empty())//该防晒霜不能用,换下一个{k++, i--;}}cout << ans << endl;return 0;
}
Moo University - Financial Aid(POJ 2010)

题目链接:Moo University - Financial Aid
参考博文:POJ 2010 Moo University – Financial Aid 题解 《挑战程序设计竞赛》

  • 题目大意:从C头奶牛中招收N头。它们分别得分score_i,需要资助学费aid_i。希望新生所需资助不超过F,同时得分中位数最高。求此中位数。
  • 思路:先将奶牛排序,考虑每个奶牛作为中位数时,比它分数低(前面的)的那群牛的学费总和lower_i(选取N/2个最少学费之和),后面的总和upper_i(选取N/2个最少学费之和)。然后从分数高往分数低扫描,满足aid_i + lower_i + upper_i <= F的第一个解就是最优解。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;const int MAX = 100005;
struct Cow
{int s, m;
};
bool cmp(Cow a, Cow b)
{return a.s>b.s;
}
struct cmp1
{bool operator ()(Cow a, Cow b){return a.m<b.m;}
};int N, C, F;
Cow c[MAX];
int lower[MAX], upper[MAX];//在前i个或后i个牛中最少的N/2个学费之和
priority_queue<Cow, vector<Cow>, cmp1>q, p;//取最大的学费int main()
{while(scanf("%d%d%d", &N, &C, &F)!=EOF){for(int i=0; i<C; i++)scanf("%d%d", &c[i].s, &c[i].m);sort(c, c+C, cmp);//分数降序int n = N/2, total = 0;//求lower的值for(int i=0; i<C; i++){if(q.size()>=n){lower[i] = total;}else{lower[i] = 0;}q.push(c[i]);total += c[i].m;if(q.size()>n){total -= q.top().m;q.pop();}}//求upper的值total = 0;for(int i=C-1; i>=0; i--){if(p.size()>=n){upper[i] = total;}else{upper[i] = 0;}p.push(c[i]);total += c[i].m;if(p.size()>n){total -= p.top().m;p.pop();}}//筛选出最终结果int ans = -1;for(int i=C; i>=0; i--){if(lower[i]+c[i].m+upper[i]<=F)//判断条件{ans = c[i].s;break;}}if(ans==-1)printf("-1\n");elseprintf("%d\n", ans);}return 0;
}

并查集

Wireless Network

题目链接:Wireless Network

  • 题目大意:一些结点需要维修,并检查一些结点是否连通。给出结点数目,结点之间的连通半径,并给出所有结点的坐标,最后给出相应操作,维修和检验联通,每一次检验均需要给出检验结果。
  • 思路:并查集题。在维修时,判断该结点是否可以与之间维修的结点并入一个集合(即连通),检验时直接用并查集检验。

代码:

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;const int MAX = 1005;
struct Node
{int x, y;
};
Node C[MAX];
int a[MAX], d, N, id, id2;
int pre[MAX], Rank[MAX];
char order;
bool check(int x1, int y1, int x2, int y2)
{if(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))<=(double)d){return true;}return false;
}void Init()
{for(int i=0; i<MAX; i++){pre[i] = i;Rank[i] = 1;}
}
int findRoot(int x)
{int r = x;while(r!=pre[r]){r = pre[r];}int j=x, i;while(j!=r){i = pre[j];pre[j] = r;j = i;}return r;
}
void join(int x, int y)
{int fx = findRoot(x), fy = findRoot(y);if(Rank[fx]>Rank[fy]){pre[fy] = fx;}else{pre[fx] = fy;if(Rank[fx]==Rank[fy]) Rank[fy]++;}
}
bool same(int x, int y)
{return findRoot(x)==findRoot(y);
}int main()
{scanf("%d%d", &N, &d);Init();for(int i=1; i<=N; i++){scanf("%d%d", &C[i].x, &C[i].y);}int k = 0;while(scanf("%c%d", &order, &id)!=EOF){if(order=='O'){a[k++] = id;for(int i=0; i<k-1; i++){if(check(C[a[i]].x, C[a[i]].y, C[id].x, C[id].y)){join(id, a[i]);}}}else if(order=='S'){scanf("%d", &id2);if(same(id, id2)){//cout << pre[id] << " " << pre[id2] << endl;printf("SUCCESS\n");}else{printf("FAIL\n");}}}return 0;
}
Find them, Catch them(POJ 1703)

题目链接:Find them, Catch them

  • 题目大意:在一个城市里有两种不同的犯罪团伙。首先输入T表示有T组测试,然后输入N和M,表示有N个罪犯(编号从1到N)而且接下来有M个操作。操作分为两种:
    1.D a b,表示编号为a和b的两个罪犯属于不同的犯罪团伙;
    2.A a b,表示询问编号为a和b的两个罪犯是否是同一个犯罪团伙或者不确定。
    对于每一个A操作,根据题意都要有相应的回答(输出)。
  • 思路:这道题目类似食物链的题目食物链(相关讲解),属于种类并查集,即无法准确确定一个类具体属于那一个种类,只能知道不同类之间的关系,这样就需要维护比原始数目大种类个数倍的数组,同时更新每一个每一个阶段的数组,维护其关系而不是维护其种类。
    本题目的并查集表示A和B分别属于两个集团的关系同时存在。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;const int MAX = 100005;
int pre[MAX*2], Rank[MAX*2];
int N = MAX-1;//并查集表示关系同时存在
void Init()
{for(int i=0; i<=2*MAX; i++){pre[i] = i;Rank[i] = 1;}
}
int findRoot(int x)
{int r = x;while(r!=pre[r]){r = pre[r];}int j=x, i;while(j!=r){i = pre[j];pre[j] = r;j = i;}return r;
}
void join(int x, int y)
{int fx = findRoot(x), fy = findRoot(y);if(Rank[fx]>Rank[fy]){pre[fy] = fx;}else{pre[fx] = fy;if(Rank[fx]==Rank[fy]) Rank[fy]++;}
}
bool same(int x, int y)
{return findRoot(x)==findRoot(y);
}int main()
{int T, N, M, a, b;char order;scanf("%d", &T);while(T--){Init();scanf("%d%d", &N, &M);for(int i=0; i<M; i++){getchar();scanf("%c%d%d", &order, &a, &b);if(order=='D'){join(a, b+N);join(a+N, b);}else if(order=='A'){if(same(a, b))printf("In the same gang.\n");else if(same(a, b+N) || same(a+N, b))//不是一个种类printf("In different gangs.\n");elseprintf("Not sure yet.\n");}}}return 0;
}

图论

Six Degrees of Cowvin Bacon(POJ 2139)

题目链接:Six Degrees of Cowvin Bacon

  • 题目大意:先根据题意建立图,然后求出每个点到其他每个点的最短距离的均值(即总和/可以到达的点的个数)
  • 思路:使用的多源最短路径中的Floyd算法。得到了所有结点之间的最短距离后,遍历每一个点的均值,然后得到最小的值就是结果。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;const int MAX = 500;
const int INF = 1<<29;
int G[MAX][MAX], a[MAX];
int cnt = 0, Min = INF, ans;
int N, M, n;int main()
{scanf("%d%d", &N, &M);for(int i=0; i<=N; i++){for(int j=0; j<=N; j++)G[i][j] = INF;}while(M--){scanf("%d", &n);for(int i=0; i<n; i++){scanf("%d", &a[i]);for(int j=i-1; j>=0; j--){G[a[j]][a[i]] = 1;G[a[i]][a[j]] = 1;}}}for(int k=1; k<=N; k++){for(int i=1; i<=N; i++){if(G[i][k]==INF) continue;for(int j=1; j<=N; j++){if(G[k][j]==INF) continue;G[i][j] = min(G[i][j], G[i][k]+G[k][j]);}}}Min = INF;double r;for(int i=1; i<=N; i++){cnt = 0, ans = 0;for(int j=1; j<=N; j++){if(i==j) continue;if(G[i][j]!=INF){ans++;cnt += G[i][j];}}Min = min(Min, cnt);//cout << cnt << endl;r = Min/(1.0*ans)*100;}Min = r;printf("%d\n", Min);return 0;
}
Wormholes(POJ 3259)

题目链接:Wormholes

  • 题目大意:农夫john发现了一些虫洞,虫洞是一种在你到达虫洞之前把你送回目的地的一种方式,FJ的每个农场,由n块土地(编号为1-n),M条路,和W个 虫洞组成,FJ想从一块土地开始,经过若干条路和虫洞,返回到他最初开始走的地方并且时间要在他离开之前,或者恰好等于他离开的时间。
  • 思路:明显的使用带负权的单源最短路径算法,所有选择Bellman_Ford算法,最后判断一下是否有负环而已。裸题。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;const int MAX = 505;
const int INF = 1<<29;
int G[MAX][MAX], d[MAX];
int F, N, M, W, En, S, E, T;
struct Edge
{int from, to, w;
}e[5005];
void Bellman_Ford(int s)
{fill(d, d+MAX, INF);d[s] = 0;int k = 0;while(1){int flag = 0;for(int i=0; i<En; i++){if(d[e[i].from]!=INF && d[e[i].to]>d[e[i].from]+e[i].w){d[e[i].to] = d[e[i].from]+e[i].w;flag = 1;}}if(!flag) break;k++;if(k>=N) break;}if(k>=N)printf("YES\n");elseprintf("NO\n");
}
int main()
{scanf("%d", &F);while(F--){En = 0;scanf("%d%d%d", &N, &M, &W);for(int i=0; i<M; i++)//双向{scanf("%d%d%d", &S, &E, &T);e[En].w = T, e[En].from = S, e[En++].to = E;e[En].w = T, e[En].from = E, e[En++].to = S;}for(int i=0; i<W; i++)//单向{scanf("%d%d%d", &S, &E, &T);e[En].w = -T, e[En].from = S, e[En++].to = E;}Bellman_Ford(1);}return 0;
}
Silver Cow Party(POJ 3268)

题目链接:Silver Cow Party

  • 题目大意:给定一些路线和花费,然后N头牛,每头牛要求按照最短路走到X处,并用最短路在返回,问这些牛中所有路线里最大值是多少。(注意是单向图)
  • 思路:可以先从X用Dijkstra,再将图的边的方向反过来,再用dijstra求一下X的单源最短路径,求二者和最大即可

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;const int MAX = 1005;
const int INF = 1<<29;
int G[MAX][MAX];
int d[MAX], d2[MAX], vis[MAX];
int N, M, X, A, B, T;void dijstra(int s, int (&d)[MAX])
{fill(d, d+N+2, INF);memset(vis, 0, sizeof(vis));d[s] = 0;while(1){int minv = INF, u;for(int i=1; i<=N; i++){if(vis[i]==0 && d[i]<minv){minv = d[i];u = i;}}if(minv==INF) break;vis[u] = 1;for(int j=1; j<=N; j++){if(vis[j]==0 && G[u][j]!=INF && d[u]+G[u][j]<d[j]){d[j] = G[u][j] + d[u];}}}
}int main()
{scanf("%d%d%d", &N, &M, &X);for(int i=0; i<=N; i++){for(int j=0; j<=N; j++){G[i][j] = INF;}}for(int i=0; i<M; i++){scanf("%d%d%d", &A, &B, &T);G[A][B] = T;}dijstra(X, d);for(int i=1; i<=N; i++){for(int j=1; j<=i; j++){swap(G[i][j], G[j][i]);}}dijstra(X, d2);int s = 0;for(int i=1; i<=N; i++){if(d[i]!=INF && d2[i]!=INF)s = max(d[i]+d2[i], s);}printf("%d\n", s);return 0;
}
Bad Cowtractors(POJ 2377)

题目链接:Bad Cowtractors

  • 题目大意:给定每条路线间需要的费用,建造一个最贵的网络线路(任意两节点都可以互相达到,但是不存在回路),求最多需要花费多少钱。
  • 思路:求最大生成树。则将边的权重取负,然后按照最小生成树计算即可。注意:使用邻接表可以方便处理结点之间有重边的情况,并且效率还高,推荐使用邻接表实现。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;/*
使用了邻接表,不需要考虑重边,因为所以的边均保存了;
如果是邻接矩阵,需要保存最大或最小的边
*/
const int MAX = 1005;
const int INF = 1<<29;
int N, M;
int A, B, C;
int vis[MAX], d[MAX];
struct Node
{int to, w;Node(int to=-1, int w=-1): to(to), w(w){}
};
vector<Node>G[MAX];void Prim()
{fill(d, d+MAX, INF);memset(vis, 0, sizeof(vis));d[1] = 0;while(1){int Minv = INF, u;for(int i=1; i<=N; i++){if(vis[i]==0 && d[i]<Minv){Minv = d[i];u = i;}}if(Minv==INF) break;vis[u] = 1;//cout << u << " " << d[u] << endl;for(int j=0; j<G[u].size(); j++){Node v = G[u][j];if(vis[v.to]==0 && d[v.to]>v.w){d[v.to] = v.w;}}}int sum = 0, flag = 0;//cout << endl;for(int i=1; i<=N; i++){if(d[i]!=INF)sum += d[i];elseflag = 1;}if(!flag)printf("%d\n", -sum);elseprintf("-1\n");
}
int main()
{cin >> N >> M;for(int i=0; i<M; i++){cin >> A >> B >> C;G[A].push_back(Node(B, -C));G[B].push_back(Node(A, -C));}Prim();return 0;
}
Out of Hay(POJ 2395)

题目链接:Out of Hay

  • 题目大意:有n个农场,贝西在1号农场,要访问其他n-1个农场,给出m条路,a b c表示a农场到b农场路程为c(两个农场间可能有多条路)。贝西会挑选最短的路径来访问完这n-1个农场。 问在此过程中贝西会经过的最大边是多大?
  • 思路:求最小生成树上的最大边,注意有重边,使用邻接表。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;/*
使用了邻接表,不需要考虑重边,因为所以的边均保存了;
如果是邻接矩阵,需要保存最大或最小的边
*/
const int MAX = 2005;
const int INF = 1<<29;
int N, M;
int A, B, C;
int vis[MAX], d[MAX];
struct Node
{int to, w;Node(int to=-1, int w=-1): to(to), w(w){}
};
vector<Node>G[MAX];void Prim()
{fill(d, d+MAX, INF);memset(vis, 0, sizeof(vis));d[1] = 0;while(1){int Minv = INF, u;for(int i=1; i<=N; i++){if(vis[i]==0 && d[i]<Minv){Minv = d[i];u = i;}}if(Minv==INF) break;vis[u] = 1;//cout << u << " " << d[u] << endl;for(int j=0; j<G[u].size(); j++){Node v = G[u][j];if(vis[v.to]==0 && d[v.to]>v.w){d[v.to] = v.w;}}}int Max = 0;//cout << endl;for(int i=1; i<=N; i++){Max = max(Max, d[i]);}printf("%d\n", Max);
}
int main()
{cin >> N >> M;for(int i=0; i<M; i++){cin >> A >> B >> C;G[A].push_back(Node(B, C));G[B].push_back(Node(A, C));}Prim();return 0;
}

数论

Dead Fraction(POJ 1930)

题目链接:Dead Fraction
参考博文:poj 1930 Dead Fraction
参考博文:POJ - 1930 Dead Fraction(简单数学推理)

Prime Path(POJ 3126)

题目链接:Prime Path

  • 题目大意:给你两个四位数m和n,求m变到n至少需要几步;每次只能从个十百千上改变一位数字,并且改变后的数要是素数。
  • 思路:使用埃氏筛法来筛选出给定范围内的所有素数,然后使用BFS搜索到达n的最短路径,使用素数数组来作为判定减枝条件。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;const int MAX = 10000;
int isPrime[MAX];
int vis[MAX];
int T, from, to;
struct Node
{int n, t;//n是数字,t是步数Node(int n=-1, int t=-1): n(n), t(t){}
};
queue<Node> q;
void getPrime()
{fill(isPrime, isPrime+MAX, 1);isPrime[1] = 0;for(int i=2; 2*i<MAX; i++){if(isPrime[i]){for(int j=2*i; j<MAX; j+=i){isPrime[j] = 0;}}}
}void bfs(int from)
{while(!q.empty()) q.pop();memset(vis, 0, sizeof(vis));q.push(Node(from, 0));vis[from] = 1;while(!q.empty()){Node u = q.front(); q.pop();if(u.n==to){printf("%d\n", u.t);return;}int k = 1;for(int i=0; i<4; i++){for(int j=0; j<10; j++)//注意从0开始{if(i==3 && j==0) continue;//首位不能是0int v = u.n/(k*10)*(k*10)+(u.n%k)+j*k;//组合成新的数字if(isPrime[v] && vis[v]==0)//判定减枝{q.push(Node(v, u.t+1));vis[v] = 1;}}k *= 10;}}printf("Impossible\n");
}int main()
{scanf("%d", &T);getPrime();while(T--){scanf("%d%d", &from, &to);bfs(from);}return 0;
}
X-factor Chains(POJ 3421)

题目链接:X-factor Chains
参考博文:POJ 3421 X-factor Chains (因式分解+排列组合)

  • 题目大意:一条整数链,要求相邻两数前一个整除后一个。给出链尾的数,求链的最大长度以及满足最大长度的不同链的数量。
  • 思路:因式分解的素因子个数即为链长。因为链中后一个数等于前一个数乘以某素因子(相当于1乘以一些素数然后得到X),所以链的数量即为这些素因子不相异的全排列数:A!/(a1!a2!a3!..)

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;const int MAX = 1<<20+1;
typedef long long LL;
LL X, p[MAX], a[MAX];int main()
{while(scanf("%lld", &X)!=EOF){int cnt = 0;//素因子分解(因为从小到大分解,得到的一定是素数)for(LL i=2; i*i<=X; i++){if(X%i==0){p[cnt] = i;a[cnt++] = 1;X /= i;}while(X%i==0){a[cnt-1]++;X /= i;}}if(X>1)//这一步容易遗忘{p[cnt] = X;a[cnt++] = 1;}//相关计算LL A = 0, ans = 1;for(int i=0; i<cnt; i++){A += a[i];}for(LL i=2; i<=A; i++){ans *= i;}for(LL i=0; i<cnt; i++){for(LL j=2; j<=a[i]; j++){ans /= j;}}printf("%lld %lld\n", A, ans);}return 0;
}
Semi-prime H-numbers(POJ 3292)

题目链接:Semi-prime H-numbers
参考博文:Semi-prime H-numbers POJ - 3292(简单打表)

  • 题目大意:定义一种数叫H-numbers,它是所有能除以四余一的数。
    在H-numbers中分三种数:
    1、H-primes,这种数只能被1和它本身整除,不能被其他的H-number整除,例如9是一个H-number,能被1,3,9整除,但3不是H-number,所以他是H-primes。
    2、H-semi-primes是由两个H-primes相乘得出的,H-semi-prime只能分解成两个H-prime数相乘。
    3、剩下的是H-composite。
    问给一个数,求1到这个数之间有多少个H-semi-primes。
  • 思路:打表得出所有H-semi-primes,然后使用数组累积得到输出结果。我的方法有点麻烦,虽然思想是一样的,还是代码能力不行。

先看别人的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define MAXN ((int)1e6 + 10)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int vis[MAXN];
int ans[MAXN];
void prime_()
{for(int i = 5; i <= MAXN; i += 4){for(int j = 5; j <= MAXN; j += 4){if(i * j > MAXN) break;//超出范围//i 与 j 都不可以被分解,换句话说就是之前没有被构造出来过//i*j一定符合4*n+1,这时的i*j定是H-semi—prime,以后再以i * j作为因子的数就不可以构成了,仔细体会下if(!vis[i] && !vis[j]) vis[i * j] = 1;//得到H-semi—prime//之前被构造出来过 所以 i * j 可以被分解成至少三个H-prime,所以不行else vis[i * j] = -1; }}int len = 0;//累积for(int i = 1; i <= MAXN; i++){ //打表,计算出1-i之间的多少个H-semi—prime数if(vis[i] == 1) len++;vis[i] = len; //有点像前缀和}
}
int main()
{int n;prime_();while(~scanf("%d", &n) && n){printf("%d %d\n", n, vis[n]);}
}

自己的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;typedef long long LL;
const int MAX = 1000002;
int h, flag[MAX], ans[MAX];
vector<int> v;
int r[MAX];//打表+筛选
void isHPrime()
{memset(flag, 1, sizeof(flag));//得到H素数,存入数组,并将H-composite标记为0for(int i=1; i<=MAX/4; i++){int j = 4*i+1;if(j>=MAX) break;if(flag[j]){for(int k=2; k<MAX; k++){if((k*j-1)%4!=0) continue;int a = (k*j-1)/4;if(4*a+1>=MAX) break;flag[4*a+1] = 0;}v.push_back(j);}}//标记所有的H_s_pint k = 0;r[k++] = 0;//作为一个初始数据方便下面的程序for(int i=0; i<v.size(); i++){for(int j=i; j<v.size(); j++){if(MAX/v[i]<v[j]) break;//很重要,不然程序会溢出LL u = v[i]*v[j];if(u<MAX && flag[u]==0)//乘积是H-composite{r[k++] = u;}elsebreak;}}sort(r, r+k);//排序,方便下面的程序//更新数量数组ans[0] = 0;for(int i=1; i<k; i++)//r的下标{for(int j=r[i-1]+1; j<r[i]; j++)//相邻r元素之间的数据赋同样的值{ans[j] = ans[j-1];}ans[r[i]] = ans[r[i]-1]+1;//加一}
}
int main()
{isHPrime();while(scanf("%d", &h)!=EOF && h){printf("%d %d\n", h, ans[h]);}return 0;
}
Raising Modulo Numbers(POJ 1995)

题目链接:Raising Modulo Numbers

  • 题目大意:(A1^B1 +A2^B2+ … +AH^BH)mod M。
  • 思路:快速幂乘,只不过每乘一次或加一次都需要模M一次。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;typedef long long LL;
LL Z, H, M, A, B;LL Pow(LL a, LL p)
{LL ans = 1, t = p;while(t){if(t&1) ans = (a*ans)%M;a = (a*a)%M;t >>= 1;}return ans;
}int main()
{scanf("%lld", &Z);while(Z--){scanf("%lld%lld", &M, &H);LL cnt = 0;for(LL i=0; i<H; i++){scanf("%lld%lld", &A, &B);cnt = (cnt+Pow(A, B))%M;}printf("%lld\n", cnt);}return 0;
}
Pseudoprime numbers(POJ 3641)

题目链接:Pseudoprime numbers

  • 题目大意:给出 p 和 a,若 a^p 和a对 p 同余且 p 不是素数,则输出 yes,否则输出 no。
  • 思路:快速幂乘,且判断素数。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;typedef long long LL;
LL p, a;int IsPrime(LL p)
{for(LL i=2; i*i<=p; i++){if(p%i==0)return 0;}return 1;
}
LL Pow(LL a, LL p)
{LL ans = 1, t = p;while(t){if(t&1) ans = (a*ans)%p;a = (a*a)%p;t >>= 1;}return ans;
}
void solve(LL a, LL p)
{if(IsPrime(p)||Pow(a, p)!=a%p)printf("no\n");elseprintf("yes\n");
}int main()
{while(scanf("%lld%lld", &p, &a)!=EOF && (a+p)){solve(a, p);}return 0;
}

挑战程序设计竞赛(第二章习题总结)相关推荐

  1. 挑战程序设计竞赛(第二章:2.5 图论)

    文章目录 RoadBlocks Conscription Layout RoadBlocks 参考博文:挑战程序设计竞赛: Roadblocks 参考博文:[dijkstra优化/次短路径]POJ32 ...

  2. 《挑战程序设计竞赛》--初级篇习题POJ部分【动态规划】

    关于基本的动态规划和经典的动态规划,在之前已经总结过了,可以温习一下: 传送门 这次是延续上次的<挑战程序设计竞赛>初级篇,总结部分poj上的练习题,主要是DP方面的练习题: 一.基础的动 ...

  3. 《挑战程序设计竞赛》--初级篇习题POJ部分【2.4 - 2.6】

    这次是延续上次的<挑战程序设计竞赛>初级篇,总结部分poj上的练习题,主要是2.4 ~ 2.6部分: 导航 2.4 加工并存储的数据结构 优先队列 Sunscreen MooUnivers ...

  4. 《挑战程序设计竞赛》--初级篇习题POJ部分【穷竭搜索+贪心】

    最近看了<挑战程序设计竞赛>初级篇,这里总结一下部分poj上的练习题,主要涉及方面为: 穷竭搜索 and 贪心算法 具体题目: 简单导航 一.穷竭搜索 二.贪心算法 一.穷竭搜索 穷竭搜索 ...

  5. 挑战程序设计竞赛(第2版)》

    <挑战程序设计竞赛(第2版)> 基本信息 作者: (日)秋叶拓哉 岩田阳一 北川宜稔 译者: 巫泽俊 庄俊元 李津羽 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787 ...

  6. 【操作指导 | 代码实现】挑战程序设计竞赛2:算法和数据结构

    书籍封面 第一章 前言 1. 本人衷心建议 ~~~~~~       如果你是一位初学者,我指的是你只会基本的 C/C++ 编程,即使编的很烂,这本书对于你算法和数据结构的提升非常有帮助,所涉及的每一 ...

  7. 《挑战程序设计竞赛》 读后感(转载)

    <挑战程序设计竞赛> 读后感 最近要开始准备面试找工作,算法是准备的重中之重,舍友推荐了<挑战程序设计竞赛>这本书.花了一周的时间大体过了一遍,该书真切地让我理解了" ...

  8. 每周一书《 挑战程序设计竞赛 (第2版)》分享!

    内容简介 <挑战程序设计竞赛(第2版)>对程序设计竞赛中的基础算法和经典问题进行了汇总,分为准备篇.初级篇.中级篇与高级篇4章.作者结合自己丰富的参赛经验,对严格筛选的110多道各类试题进 ...

  9. 挑战程序设计竞赛书的题解总结(连载中)

    写在前面的话(前言) 每天无所事事真是无趣啊,作为一个将来要征战acm的优秀青年,我感到有刷点什么的必要了! 毕竟同级的acm大佬们真是太强了,跟着老师走肯定会一直被暴打下去..去oj上刷题呢,又感觉 ...

最新文章

  1. Oracle内部错误:ORA-00600:[4097]一例
  2. MySQL-高可用架构探索
  3. SqlServer 0和空字符串''等价?-----类型的隐式转换问题
  4. modbus poll\slave
  5. python协成_Python协程技术的演进
  6. 使用encodeURl()进行编解码
  7. 图片标注尺寸_AutoCAD图纸与测量尺寸不一样怎么办
  8. vcsa上RVC查看vsan状态的命令
  9. python 从小白到大牛这本书好嘛_《Python从小白到大牛》又一本零基础入门书
  10. chrome插件系列一:Secure Shell(替代ssh客户端)
  11. 微信小程序入门1-小程序代码构成json
  12. 【openGauss】gsql客户端工具(二)gsql客户端工具之Data Studio客户端工具
  13. 五笔字根表识别码图_五笔输入法口诀(五笔字根表快速记忆图)
  14. Java 题目:有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
  15. Mecanim 工作流
  16. HTML+JS 实现 input 框回车事件
  17. 实用计算机理论基础知识试题及答案,计算机基础知识试题库及答案(5)
  18. 攻略:大陆人成立香港公司以后如何运营?
  19. wampserver常见问题
  20. 程序员吐槽:四年被三家公司裁员补偿,网友:去BAT转转?

热门文章

  1. 有史以来最漂亮的游戏机
  2. oracle如何设置将菜单个性化,Oracle EBS Form个性化
  3. 最新ui设计课程视频源码资料网盘分享
  4. qt messagebox退出程序_删除英雄联盟LOL电视台,QT语音直播,TP安全中心
  5. 哈理工计算机学院学生会技术部,[大学学生会技术部工作学期总结]学生会技术部...
  6. Springboot项目打包成jar没有jsp文件
  7. Adobe Illustrator CC 2023(AI2023)安装教程与下载方式
  8. Android黑屏死机--充电运行土豆视频【.4.4】》播放视频中黑屏死机》手动按电源键开机显示电量为6%
  9. 机器学习笔记-Logistic分类
  10. 翻译D23(附AC码 POJ 1551:Sumsets)