第一题 光棍
题面http://oj.daimayuan.top/problem/676
实际上就是用n个1除x,算出n和除数,最开始想不会要写高精,后面发现只用除法,就可以用个res和x直接模拟手算不用高精存进字符串
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
int main() {
int x;
scanf("%d", &x);
int n = 1;
int res = 1;
while (res < x) {
n++;
res *= 10;
res++;
}
while (true)
{
while (res < x) {
printf("0");
n++;
res *= 10;
res++;
}
printf("%d", res / x);
if (res % x == 0) {
//n++;
break;
}
else {
n++;
res = res - res / x * x;
res *= 10;
res++;
}
}
printf(" %d",n);
}
逻辑重新理了下,发现res直接更新成对x取余就行,原本还要判断res会不会比x小导致要商0,再补1
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
int main() {
int x;
scanf("%d", &x);
int n = 1;
int res = 1;
while (res < x) {
n++;
res *= 10;
res++;
}
while (true)
{
printf("%d", res / x);
res %= x;
if (res == 0)break;
res *= 10;
res++;
n++;
}
printf(" %d",n);
}
第二题 碰撞2
题面 http://oj.daimayuan.top/problem/705
按照行坐标存map,然后判断每行会不会撞就行
易得,当上一个向右,现在的向左才会装,所以写个last判相乘为不为负且上一个向右就行
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
struct node
{
int x; int y;
};
node ori[maxn];
int main() {
map<int, map<int, int>> mymap;
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
node temp;
temp.x = x;
temp.y = y;
ori[i] = temp;
}
char head[maxn];
scanf("%s", head);
for (int i = 0; i < n; i++) {
if (head[i] == 'R') {
mymap[ori[i].y][ori[i].x] = 1;
}
else {
mymap[ori[i].y][ori[i].x] = -1;
}
}
for (map<int, map<int, int>>::iterator it1 = mymap.begin(); it1 != mymap.end(); it1++) {
if (it1->second.size() == 1) {
continue;
}
int yes = 0;
for (map<int, int>::iterator it2 = it1->second.begin(); it2 != it1->second.end(); it2++) {
if (yes * it2->second == -1&&yes==1) {
printf("Yes");
return 0;
}
yes = it2->second;
}
//if (yes != it1->second.size() && (0 - yes) != it1->second.size()) {
// if (it1->second.begin()->second != -1&&yes!= it1->second.size()-1) {
// printf("Yes");
// return 0;
// }
//}
}
printf("No");
}
第三题 优美!最长上升子序列
题面:http://oj.daimayuan.top/problem/671
常规方法是枚举1到i有没有比i小的数,取最长dp,但这里时间复杂度过不了,只能换种思路
i还是从1到n开始遍历,但是是从i往后更新,而不是从0到i找最大更新i,
首先,i的所有倍数都能整除i,所以j=i*2,j<=n;j+=i;
j下标对应的ori都是能取得,之后只要ori[j]>ori[i]就更新就行了,
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int ori[maxn];
int dp[maxn];
int main() {
int t;
scanf("%d", &t);
while (t--)
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &ori[i]);
dp[i]=1;
}
for (int i = 1; i <= n; i++) {
//int temp = 0;
for (int j = i * 2; j <= n;j+=i) {
if (ori[j] > ori[i]) {
dp[j] = max(dp[i] + 1, dp[j]);
}
}
}
printf("%d\n", *max_element(dp + 1, dp + 1 + n));
}
}
第四题 巨大的牛棚
题面:
居然是个二维dp,幸好我没傻乎乎的写bfs摁搞,那样必超时
dp,dp[i][j]表示包含(i,j)的最大面积
然后若他左,左上,上三个的面积一样,那它就等于期中之一加1,若不等,就取期中最小加1,dp[i][j]实际上也代表以这个点为右下角,有多大的面积是空的
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int ori[maxn][maxn];
int dp[maxn][maxn];
int main() {
int n;
int t;
scanf("%d%d", &n, &t);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = 1;
}
}
for (int i = 0; i < t; i++) {
int x, y;
scanf("%d%d", &x, &y);
dp[x][y] = 0;
}
int maxn = -1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (dp[i][j] == 0)continue;
//if (dp[i - 1][j] == dp[i - 1][j - 1] && dp[i][j - 1] == dp[i - 1][j]) {
// dp[i][j] = dp[i - 1][j] + 1;
//}
//else {
// dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1;
//}
dp[i][j] = min(min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
maxn = max(maxn, dp[i][j]);
}
}
cout << maxn;
}
第五题 高利贷
题面:http://oj.daimayuan.top/course/11/problem/708
列了个求和公式就,
m/n=p/(1+p)^k求和
本来想直接解通项的,但太麻烦了,于是二分走起
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
double n, m, k;
double sum(double p) {
double summ = 0;
for (int i = 1; i <= k; i++) {
summ += m / (pow(1.0 + p, i));
}
return summ;
}
int main() {
scanf("%lf%lf%lf", &n, &m, &k);
double l = 0, r = 1;
while ( r-l>(1e-6))
{
double mid = (l + r) / 2;
if (sum(mid) > n) {
l = mid;
}
else {
r = mid;
}
}
printf("%lf", l);
}
第六题 背包
题面http://oj.daimayuan.top/course/11/problem/710
1.将所有物品从小到大排序,且大于M的直接舍弃
1.最小的数大于w/2,YES
2.最小的数小于w/2
1.所有数累加小于w NO
2.累加存在大于w/2 YES
这种情况证明
假设前面和为x(x<w/2),加下一个数Y后总和大于w/2
若总和<w,YES
若总和>w
因为Y<w,所以y+x>w,x<w/2,可证出Y>w/2,即得证YES
然后要注意下整形除法囤小数的问题
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5;
int main() {
int t;
cin >> t;
while (t--)
{
int len = 0;
int ori[maxn];
long long w;
int n;
cin >> n >> w;
for (int i = 0; i < n; i++) {
int temp;
cin >> temp;
if (temp > w)continue;
ori[len++] = temp;
}
sort(ori, ori + len);
long long sum = 0;
for (int i = 0; i < len; i++) {
sum += ori[i];
if (sum >= (w / 2+w%2)) {
cout << "YES" << endl;
break;
}
}
if (sum < (w / 2+w%2)) {
cout << "NO" << endl;
}
}
}
第七题 三回文序列
题面http://oj.daimayuan.top/course/11/problem/711
112232111为例,最开始想的是求出每个数出现的前缀合
121213345,之后遍历一遍假设现在遍历到第三个1,往前找1,并且记录这期间最长的其他数字(前缀和直接减),但是发现这个情况过不了11133333112232111,我们要的是头尾两个1,但之前的方法找到连着两个1的就停了,除非继续遍历到头,
后面改了改,
numo为每个数字前缀和,方便取出特定区间某个数的个数,先想得是外侧遍历a是几(记为i),内层遍历两边a的个数j,从1到a的个数的一半,但这个方法找符合j长度的区间起始点s和e很慢,每次都要从头开始找符合长度的,超时,后面发现不用每次都要找s和e,直接两边长度+=2,之后找下一个=i的两边就行了
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int numo[maxn][27];
int ori[maxn];
int main() {
int t;
scanf("%d", &t);
while (t--)
{
int n;
int num[27] = { 0 };
//int last[27] = { 0 };
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &ori[i]);
num[ori[i]]++;
numo[i][ori[i]]++;
for (int j = 1; j <= 26; j++) {
numo[i + 1][j] = numo[i][j];
}
}
int maxo = 1;
for (int i = 1; i <= 26; i++) {
if (num[i] == 0)continue;
//for (int j = 1; j <= num[i] / 2; j++) {
// int s;
// //int last;
// //for (int k = n-1; k >= 0; k--) {
// // if (ori[k] == i) {
// // s = k;
// // break;
// // }
// //}
// //for (int k = s; k >= 0; k--) {
// // if (ori[k] != i)continue;
// // if (numo[s][i] - numo[k][i] == j) {
// // s = last;
// // break;
// // }
// // last = k;
// //}
// for (int k = n - 1; k >= 0; k--) {
// if (numo[n - 1][i] - numo[k][i] == j) {
// s = k + 1;
// break;
// }
// }
// int e = 0;
// /*for (int k = e; k < n; k++) {
// if (ori[k] == i) {
// e = k;
// break;
// }
// }
// for (int k = e; k <n; k++) {
// if (ori[k] != i)continue;
// if (numo[k][i] - numo[e][i] == j) {
// e = last;
// break;
// }
// last = k;
// }*/
// for (int k = 0; k < n; k++) {
// if (numo[k][i] - 0 == j) {
// e = k;
// break;
// }
// }
// for (int k = 0; k <= 26; k++) {
// maxo = max(maxo, numo[s - 1][k] - numo[e][k] + j * 2);
// }
//}
int s = 0, e = n - 1;
int liangbian = 0;
while (s < e)
{
while (ori[s] != i)
{
s++;
}
while (ori[e] != i)
{
e--;
}
if (s >= e) {
break;
}
liangbian += 2;
for (int k = 0; k <= 26; k++) {
maxo = max(maxo, numo[e - 1][k] - numo[s][k] + liangbian);
}
s++;
e--;
}
}
//maxo = max(maxo, *max_element(num+1, num + 27));
printf("%d\n", maxo);
}
}
第八题 简单的异或问题
题面http://oj.daimayuan.top/course/11/problem/732
咋一看挺难仔细一分析,实际上不难,首先给的范围是偶数个,因为含0,之后算几个数,其一是偶数开头,相邻两个数异或,4,5和6,7,你会发现所有2n和2n+1的数异或都等于1,1和1异或等于0。于是我们分析
0123456789.。。。。两两一组,共用2^m/2组,除掉含n的那一组,其他组异或完只有0和1两种可能
若为0,他只能和含n那一组的n异或,若为1,只能和含n的那一组的另一个数异或,这个是有另一个规律a^b=a+b得到的,所以共有2m-1种
一些特例
n=1,m=1,ans=2
n=0,m=1,ans=1;
若n=0,其他组异或完为1,即可和1异或,也可和0异或,所以要+1,又因为范围为偶数个,全部异或完必为1,所以n=0就要++。4
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
ll n, m;
scanf("%lld%lld", &n, &m);
ll ans = pow(2, m);
ans--;
if (n == 0)ans++;
if (n == 0 && m == 1)ans = 1;
if (n == 1 && m == 1)ans = 2;
printf("%lld", ans);
}
第九题 子串的循环挪动
题面:http://oj.daimayuan.top/course/11/problem/734
纯纯模拟题,把k%=(r-l+1)去掉重复旋转的情况减减复杂度就行
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
char str[10010];
scanf("%s", &str);
int len = strlen(str);
int ori[10010];
for (int i = 1; i <= len; i++) {
ori[i] = i;
}
int n;
scanf("%d", &n);
while (n--)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
int lennow = r - l + 1;
k %= lennow;
if (k == 0)continue;
int temp[10010];
int templen = 0;
for (int i = l; i <= r; i++) {
temp[templen++] = ori[i];
}
for (int i = 0; i <k; i++) {
ori[l+i] = ori[r-(k-i-1)];
}
for (int i = 0; i < lennow - k; i++) {
ori[l + i + k] = temp[i];
}
}
for (int i = 1; i <= len; i++) {
printf("%c", str[ori[i] - 1]);
}
}
第十题 弗拉德和糖果 II
题面http://oj.daimayuan.top/problem/677
算博弈论吧,我们需要知道最多的糖果数maxn和去掉这个的所有的糖果数sum
最极限的情况是maxn==sum+1
这种12121213131314151,其他糖果一字排开,在多一个1无论如何也不行
另一种极限情况是所有种类相等
123123123
至于其他情况都符合,你可以把多出来的其他糖果插进其他不同的糖果里,因为我们规定了最多的糖果是maxn,所以不纯在不够插的情况。
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
int n;
scanf("%d", &n);
ll temp, sum=0,maxn=0;
for (int i = 0; i < n; i++) {
scanf("%lld", &temp);
sum += temp;
maxn = max(temp, maxn);
}
sum -= maxn;
if (maxn <= sum + 1) {
printf("YES");
}
else {
printf("NO");
}
}