湖南多校对抗赛(2015.03.28)

来源:互联网 发布:型材下料软件破解版 编辑:程序博客网 时间:2024/04/29 10:13

Problem A

题意:
有2种矩形1*x和2*x, 用最小的矩形2*m来把这些框住,使得m最小,输出最小的mn个矩阵 下面n行给出给出一些矩形,矩形规模都是1*x或者2*x,且矩阵不能旋转重叠。

思路:

因为要用2*m的矩形来框住,那么对于2*x的矩形,直接累加,对于1*x的矩形,把这些1*x的矩形尽量均分成两堆,那么高的那堆就是放1*x的最低需要的高度。那么可以用背包来求出这个最低高度。

#include<iostream>#include<string>#include<stdio.h>#include<cmath>#include<algorithm>#include<cstring>using namespace std;const int MAXN = 100005;int dp[MAXN], q[MAXN];int main(){int t;scanf("%d", &t);while (t--){int n, ans = 0, m = 0, maxdp = 0;scanf("%d", &n);memset(dp, 0, sizeof(dp));for (int i = 0; i<n; i++){int l, r;scanf("%d%d", &l, &r);if (l == 2){m += r;}else{q[ans++] = r;maxdp += r;}}for (int i = 0; i<ans; i++){for (int j = maxdp / 2; j >= q[i]; j--){dp[j] = max(dp[j], dp[j - q[i]] + q[i]);}}int maxm = max(dp[maxdp/2],maxdp - dp[maxdp / 2]);printf("%d\n", maxm + m);}return 0;}


Problem B

题意:
第一行输入n, (x, y), (c1, c2)一个二维平面,开始人在(0,0),终点在(x, y), 平面上有2种地形:陆地和河流,在陆地上走花费为c1每单位,河流上走花费为c2每单位。下面n行给出n条河流。xi, wi, 河流是无限长,宽度为wi,且平行于y轴,从xi点开始。

思路:
把河流都集中在右边。然后设这个人是在y=d穿过河流的,则这个人一定会走出2条折线。列出花费与d的方程,会发现由2个带根号的式子相加。分别画出这两个式子的曲线,合成函数就是一个单峰函数,所以三分答案。

#include <iostream>#include <string>#include <algorithm>#include <cmath>using namespace std;const int INF = 0x3f3f3f3f3f;double calc(int c1, int c2, int x, int y, int rw, double ph){double bx = rw, by = ph, ux = x - rw, uy = y - ph;return sqrt(bx * bx + by * by) * c2 + sqrt(ux * ux + uy * uy) * c1;}int main(){int n, x, y, c1, c2;while (~scanf("%d%d%d%d%d", &n, &x, &y, &c1, &c2)){int rw = 0;for (int i = 0; i < n; i++){int xi, wi;scanf("%d%d", &xi, &wi);if (xi + wi <= x)rw += wi;else if (xi <= x && xi + wi >= x)rw += (x - xi);}if (y <= 100){double ans = INF;for (double i = 0; i <= y; i += 0.01)ans = min(ans * 1.0, calc(c1, c2, x, y, rw, i));printf("%.2lf\n", ans);continue;}//粗扫描double mid = 0;double last2 = calc(c1, c2, x, y, rw, 0), last = calc(c1, c2, x, y, rw, 1), now;for (double i = 2; i <= y; i++){now = calc(c1, c2, x, y, rw, i);if (last2 > last && last < now){mid = i;break;}last2 = last;last = now;}//细扫描double ans1 = INF;for (double i = 0; i <= 10; i += 0.01)ans1 = min(ans1, calc(c1, c2, x, y, rw, i));double ans2 = INF;for (double i = y - 10; i <= y; i += 0.01)ans2 = min(ans2, calc(c1, c2, x, y, rw, i));double ans3 = INF;for (double i = mid - 5; i <= mid + 5; i += 0.01)ans3 = min(ans3, calc(c1, c2, x, y, rw, i));printf("%.2lf\n", min(ans1, min(ans2, ans3)));}return 0;}


Problem C

题意:
给定n条线段(保证是1-n首尾相接的) 常数D从第一条线段左边开始,沿着线段每个长度D输出这个点的坐标(相当于一个人在线段上运动了D长度,就输出这个人的坐标)若D大于所有线段长度,则输出No Such Points.


Problem D

题意:
给出3个长度相等且长度是偶数的字符串a,b,c
在a串中取一半的字母,b串中取一半的字母,问任意顺序能否拼接出c串

#include <iostream>#include <string>#include<stdio.h>#include<cmath>#include<algorithm>#include<queue>#include <cstring>using namespace std;const int MAXN=10005;#define INF 0x3f3f3f3ftypedef long long LL ;char a[200005] , b[200005] , c[200005] ;int anum[28] , bnum[28] , cnum[28] ;int main(){#ifdef acfun_afreopen("input.txt","r",stdin) ;#endifwhile(~scanf("%s%s%s",a,b,c)){int l = strlen(a);memset(anum , 0 , sizeof(anum)) , memset(bnum , 0 , sizeof(bnum)) , memset(cnum , 0 ,sizeof(cnum)) ;for(int i = 0 ; i < l ; ++i){int index ;index = a[i] - 'A', anum[index]++ ;index = b[i] - 'A', bnum[index]++ ;index = c[i] - 'A', cnum[index]++ ;}int flag = 1 ;for(int i = 0 ; i < 26 ; ++i){if(anum[i] > l / 2)anum[i] = l / 2 ;if(bnum[i] > l / 2)bnum[i] = l / 2 ;if(anum[i] + bnum[i] < cnum[i]){flag = 0 ;break ;}}if(!flag)puts("NO") ;else{LL maxn = 0 , minn = 0 ;for(int i = 0 ; i < 26 ; ++i){if(cnum[i] > bnum[i])minn += cnum[i] - bnum[i] ;if(anum[i] >= cnum[i])maxn += cnum[i] ;elsemaxn += anum[i] ;}if(minn <= (l / 2) && (l / 2) <= maxn)puts("YES");elseputs("NO");}}return 0;}


Problem E

题意:
给出一个序列删除任意一段连续的数(也可以不删除)使得删完后 最长严格递增子段(序列必须是连续的)最长,输出这个长度


Problem F

题意:

一共n个数字,如果两个数字数值加起来是素数,那么这两个数字就能成为一对,每个数字只能和另一个数字成为一对,问最多能组多少对
思路:

比例裸的二分图最大匹配问题,不过数字比较大,一般的素数判断会超时,此处需要用到拉宾米勒测试,速度快,而且可以判断 <2^63的数
任何如果一个数与另一个数能成为一对,那么就连一条无向边,然后求最大匹配,因为是无向边,所以结果需要除以2

#define _CRT_SECURE_NO_DEPRECATE#include<algorithm>#include<cstdio>#include<cstring>#include<cmath>#include<vector>#include<string>#include<stdlib.h>#include<iostream>using namespace std;typedef long long int LL;const int MAXN = 105;const int S = 20;vector<int>map[MAXN];int vis[MAXN], lin[MAXN],cont;LL num[MAXN];LL mult_mod(LL a, LL b, LL mod) {a %= mod;b %= mod;LL ans = 0;while (b){if (b & 1){ans = ans + a;if (ans >= mod)ans = ans - mod;}a = a << 1;if (a >= mod) a = a - mod;b = b >> 1;}return ans;}LL pow_mod(LL a, LL b, LL mod) {LL ans = 1;a = a%mod;while (b){if (b & 1){ans = mult_mod(ans, a, mod);}a = mult_mod(a, a, mod);b = b >> 1;}return ans;}bool check(LL a, LL n, LL x, LL t){LL ret = pow_mod(a, x, n);LL last = ret;for (int i = 1; i <= t; i++){ret = mult_mod(ret, ret, n);if (ret == 1 && last != 1 && last != n - 1) return true;last = ret;}if (ret != 1) return true;else return false;}bool Miller_Rabin(LL n){if (n<2)return false;if (n == 2) return true;if ((n & 1) == 0) return false;LL x = n - 1;LL t = 0;while ((x & 1) == 0) { x >>= 1; t++; }for (int i = 0; i<S; i++){LL a = rand() % (n - 1) + 1;if (check(a, n, x, t))return false;}return true;}void init(){for (int i = 0; i <= cont; i++){map[i].clear();}memset(lin, -1, sizeof(lin));}bool find(int x){for (int i = 0; i < map[x].size(); i++){if (!vis[map[x][i]]){vis[map[x][i]] = 1;if (lin[map[x][i]] == -1 || find(lin[map[x][i]])){lin[map[x][i]] = x;return true;}}}return false;}int MaxMatch(int n){int res = 0;for (int i = 0; i < n; i++){memset(vis, 0, sizeof(vis));if (find(i)){res++;}}return res;}int main(){while (scanf("%d", &cont)!=EOF){init();for (int i = 0; i < cont; i++){scanf("%lld", &num[i]);}for (int i = 0; i < cont; i++){for (int j = i + 1; j < cont; j++){if (Miller_Rabin(num[i] + num[j])){map[i].push_back(j);map[j].push_back(i);}}}printf("%d\n", MaxMatch(cont)/2);}return 0;}


Problem G

题意:

给定n,k,然后给你一个n个数,求一个最长连续子序列长度,该连续子序列保证序列里面的最大值-最小值<=k。输出这个串的最长的长度。

思路:

尺取法,利用set这个数据结构,自带排序,直接模拟

#include<iostream>#include<string>#include<stdio.h>#include<cmath>#include<algorithm>#include<queue>#include<cstring>#include<set>using namespace std;const int MAXN = 10005;#define INF 0x3f3f3f3ftypedef long long LL;LL a[10005], n, k;set<LL> s;int main(){while (~scanf("%lld%lld", &n, &k)){for (int i = 0; i < n; ++i){scanf("%lld", &a[i]);}s.clear();int j = 0, ans = 0;for (int i = 0; i < n; ++i){s.insert(a[i]);while (*s.rbegin() - *s.begin() > k){s.erase(s.find(a[j++]));}ans = max(ans, i - j + 1);}printf("%d\n", ans);}return 0;}


Problem H

题意:

n个操作:1 val 在集合中插入val   2 查询当前集合 通过任意数求和不能得到的最小正整数
思路:

看到题目就想到set这个数据结构,但是插入的数据有可能相同,所以需要用到multiset。然后找不能得到的最小正整数,相当于求一个前缀和,假设这个前缀和的结尾是i,那么最小不能得到的正整数是a[1]+a[2]+a[3]....+a[i]<ans<a[i+1]。 ans就是答案,相当于以为的a[i]结尾的前缀和a[i+1]之间至少相差1,那么这个相差的位置就是答案。

#include<iostream>#include<stdio.h>#include<cstring>#include<string>#include<set>#include<map>#include<queue>#include<stack>#include<algorithm>#include<cmath>#include<stdlib.h>using namespace std;int main(){int n;while (~scanf("%d", &n)){multiset<int>se;multiset<int>::iterator it;  int op,x;long long int ans=0;for(int i=1;i<=n;i++){scanf("%d",&op);if(op==1){scanf("%d",&x);se.insert(x);while(!se.empty()){it=se.begin();if(*it>ans+1){break;}ans+=*it;se.erase(it);}}else{printf("%lld\n",ans+1);}}}return 0;}


Problem I

题意:

给定一个n和一个序列b,b[i]代表:i前面有b[i]个数比他大。问存不存一个序列使得满足要求,如果存在输出这个序列,否则输出No solution。

思路:

模拟/树状数组/线段树。以“1 2 0 1 0”为例,b[1]=1,代表1所在的位置之前有一个数比他大,那么他就在最终序列的空位置的b[1]+1的位置。 比如初始最终序列=“x x x x x”那么1就在b[1]=1+1=2.即当前第二个空位。最终序列=“x 1 x x x”,b[2]=2,那么2就在当前序列的b[2]=2+1=3个空位,即最终序列=“x 1 x 2 x”,b[3]=0+1=1,那么3就在当前空位的第一个位置即最终序列=“3 1 x 2 x”.。以此类推。最终得到最终序列=“3 1 5 2 4”,相当于找空位插入。如果对应的空位已经有元素,或者位置超出n,则说明不存在这样的序列。我这里用的是树状数组求前缀和。比如b[2]是说明2插入的位置是第三个空位,那么找到一个点的前缀和是3即可以插入。

#include<iostream>#include<stdio.h>#include<cstring>#include<string>#include<set>#include<map>#include<queue>#include<stack>#include<algorithm>#include<cmath>#include<stdlib.h>using namespace std;#define Lowbit(x)(x&(-x))const int MAXN = 10005;int c[MAXN],n,b[MAXN],ans[MAXN],vis[MAXN];void updata(int x, int val){for (int i = x; i <= n; i += Lowbit(i)){c[i] += val;}}int sum(int x){int s = 0;for (int i = x; i>0; i -= Lowbit(i)){s += c[i];}return s;}int main(){while (~scanf("%d", &n)){bool k=false;memset(vis,0,sizeof(vis));memset(c,0,sizeof(c));for (int i = 1; i <= n; i++){scanf("%d", &b[i]);c[i] = Lowbit(i);}for (int i = 1; i <=n; i++){int pos = b[i]+1;int l=1, r=n;while (r >= l){int mid = (r + l) / 2;if (sum(mid) >= pos){r = mid - 1;}else{l = mid + 1;}}if(vis[l]||l>n){k=true;break;}ans[l] = i;vis[l]=1;updata(l, -1);}if(k){printf("No solution\n");}else{for (int i = 1; i <= n; i++){printf("%d", ans[i]);if (i != n){printf(" ");}else{printf("\n");}}}}return 0;}



Problem J

题意:

给出n和x,求出1^x+2^x+...+n^x

思路:

快速幂

#include <iostream>#include <string>#include<stdio.h>#include<cmath>#include<algorithm>#include <cstring>using namespace std;const int MAXN = 100005;typedef long long LL;LL pow_(LL x, LL pos){if (!pos){return 1;}LL sum = 1;while (pos){if (pos % 2){sum = (sum * x) % 1000000007;}x = (x * x) % 1000000007;pos /= 2;}return sum;}int main(){LL n, m;while (~scanf("%lld%lld", &n, &m)){LL ans = 0;for (int i = 1; i <= n; ++i){ans = (ans + pow_(i, m)) % 1000000007;}printf("%lld\n", ans);}return 0;}



0 0
原创粉丝点击