第一题快快变大
给定一个长度为 n的数组 a1,a2,…,an,接下来进行 n−1次操作每次选择一个下标 x,将 ax 和 ax+1合并成ax×ax+1mod1000003,并且你会获得 (ax−ax+1)2的分数。所以每次操作后,数组的长度将会减 1,当最后只剩下一个元素时停止操作。输出最终能获得的最大分数。
输入格式
第一行一个数字 n接下来一行 n个整数 a1,a2,…,an
输出格式
一个数,表示答案。
样例输入
3
1 2 3
样例输出
26
首先,不存在按大小顺序来和并的简单规律,有很多简单的反例可以证明,就不说了,之后考虑dp,dp[i][j]表示从i到j这个区间内可以得到的最大分数,之后按照dp的思想,从区间长为2的区间推到n,先假设我们已经推完所有区间长为3的区间,对于区间长为4的,如dp[1][4],要在三种情况里取大,
1.先合12,34,再合并;
2.先13,再和4;
3.先24,再合1。
对于任意一段区间,合完后他们等于区间内所有数之积,所以dp[i][j]=max(dp[i][j],dp[i][k] + dp[k + 1][j] + (cheng[i][k]-cheng[k+1][j])^2),cheng表示i到j乘积,k从i+1递增到i+len-2。
#include <bits/stdc++.h>
using namespace std;
const int mod = 1000003;
int ori[400];
long long cheng[400][400];
long long dp[400][400] = { 0 };
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> ori[i];
}
for (int i = 1; i <= n; i++) {
cheng[i][i] = ori[i];
for (int j = i+1; j <= n; j++) {
cheng[i][j] = (cheng[i][j-1] * ori[j]) % mod;
}
}
for (int len = 2; len <= n; len++) {
for (int i = 1; i + len-1 <= n; i++) {
int j=i+len-1;
for(int k=i;k<j;k++){
dp[i][j]=max(dp[i][j],dp[i][k] + dp[k + 1][j] + (long long)pow((cheng[i][k]-cheng[k+1][j]),2));
}
}
}
cout << dp[1][n];
}
饿饿 饭饭2
题面http://oj.daimayuan.top/problem/561
第一版想法,求所有数的最大公约数,之后每个数除公约数,之后对2和3取余,若有一个为0,就能过,但发现,对于这组数据,20,1,4公约数是1,20取余2为0,但1和4明显乘不出20,所以要修改为每个数除公约数后的商只有2和3的指数构成,于是对于商,我们先除尽里面的2和3,若剩下来的数不为1,代表这个数不行。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 10;
ll ori[maxn];
ll yue(ll a, ll b)
{
while (1)
{
ll t = a % b;
if (t == 0)
{
break;
}
else
{
a = b;
b = t;
}
}
return b;
}
int find(int x){
while (x%2==0)
{
x/=2;
}
while (x%3==0)
{
x/=3;
}
if(x!=1){
return 1;
}else{
return 0;
}
}
int main() {
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> ori[i];
}
if (n == 1) {
cout << "YES" << endl;
continue;
}
ll maxyue = yue(ori[0],ori[1]);
for (int i = 2; i < n; i++) {
if (ori[i] % maxyue == 0) {
continue;
}
else {
maxyue = yue(maxyue, ori[i]);
}
}
//cout<<maxyue<<endl;
// if (maxyue == 1) {
// cout << "NO" << endl;
// continue;
// }
for (int i = 0; i < n; i++) {
int temp = ori[i] / maxyue;
if(temp==1)continue;
if (find(temp)) {
cout << "NO" << endl;
goto end;
}
}
cout << "YES" << endl;
end:;
}
}
第三题
题面http://oj.daimayuan.top/problem/563
想出两个方法,第一个卡一个点tle,感觉改改应该能过
1.dp[i][j]记录i到j的个数,总结出来的转移方程是len是区间长dp[i][i+len-1]=dp[i][i+len-2] + dp[i+1][i+1+len-2] - dp[i+1][i+1+len-3]+ geshu(i, len);前两项表示长为len-1的两个个数,如求1到4,前两项就是1到3和2到3的个数,第三项是前两项重合的个数,第四项从1到4的所有字母个数,对于geshu,基于前缀和,想出来一个方法,用一数组zimu[n][26]记录到第i个字符各个字母有多少个,之后一个区间的字符数等于区间末的所有字母数-区间头的所有字母数。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int zimu[maxn][26];
int dp[3][maxn];
int geshu(int j, int len) {
int num = 0;
if (j == 0) {
for (int i = 0; i < 26; i++) {
if (zimu[j + len - 1][i]> 0) {
num++;
}
}
}
else {
for (int i = 0; i < 26; i++) {
if (zimu[j + len - 1][i] -zimu[j - 1][i] > 0) {
num++;
}
}
}
return num;
}
int main() {
//ios::sync_with_stdio(false);
//cin.tie(0);
//cout.tie(0);
char a[maxn];
scanf("%s", &a);
int long_a = strlen(a);
for (int i = 0; i < long_a; i++) {
zimu[i][a[i] - 'a']++;
copy(begin(zimu[i]), end(zimu[i]), begin(zimu[i + 1]));
}
int now = 2;
int last = 1;
int lastlast = 0;
for (int i = 0; i < long_a; i++) {
dp[last][i] = 1;
dp[lastlast][i] = 0;
}
int num = 2;
while (num <= long_a)
{
for (int i = 0; i < long_a - num + 1; i++) {
dp[now][i] = dp[last][i] + dp[last][i + 1] - dp[lastlast][i + 1] + geshu(i, num);;
}
lastlast = last;
last = now;
num++;
now = now == 2 ? 0 : now + 1;
}
cout << dp[last][0];
}
第二种,贡献的想法
pre[n]存的是与第i个字符相同的前面最近的下标,没有为0,
下标从1开始,以ababcabc为例,由于不限制只出现一次,所以从i字符一直到结束的所有子串都会被贡献一个第i个字符。第一个字母a对以下标1开头到结束的所有子串都有贡献,而对于第二个字母a而言,从下标2和下标3开头的所有子串,a都有贡献,这些子串都至少有一个a,但不能有下标1开头的,因为下标1开头的已经贡献过了,所以第i个字母贡献给了(i-pre[i])(n+1-i)个子串,这些子串的字母数都加1。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int main() {
int pre[maxn] = { 0 };
int now[26] = { 0 };
string a;
cin >> a;
int len = a.length();
a = "0" + a;
for (int i = 1; i <= len; i++) {
pre[i] = now[a[i] - 'a'];
now[a[i] - 'a'] = i;
}
long long sum = 0;
for (int i = 1; i <= len; i++) {
sum += (i - pre[i]) * (len + 1 - i);
}
cout << sum;
}
第四题 蒟蒻
题面http://oj.daimayuan.top/course/11/problem/605
万能的优先队列和map!kami!
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int again_w[maxn];
int again_t[maxn];
priority_queue<int, vector<int>, greater<int>> sw;
priority_queue<int, vector<int>, greater<int>> st;
map<int, int> tw;
map<int, int>wt;
long long sum = 0;
int main() {
int n;
cin >> n;
while (n--)
{
int op;
cin >> op;
if (op == 1) {
int w, t;
cin >> w >> t;
if (again_w[w] == 1 || again_t[t] == 1) {
continue;
}
sum += w;
again_t[t] = 1;
again_w[w] = 1;
tw[t] = w;
wt[w] = t;
sw.push(w);
st.push(t);
}
else if (op == 2) {
int top = sw.top();
while (again_w[top]==0)
{
sw.pop();
top = sw.top();
}
again_w[top] = 0;
again_t[wt[top]] = 0;
sw.pop();
sum -= top;
}
else {
int top = st.top();
while (again_t[top] == 0)
{
st.pop();
top = st.top();
}
again_t[top] = 0;
again_w[tw[top]] = 0;
st.pop();
sum -= tw[top];
}
}
cout << sum;
return 0;
}
第五题 锦标赛
题面http://oj.daimayuan.top/course/11/problem/80
我们把数列从小到大排一个序,对于第i个数,他获胜的理想情况是,比他小的都输给他,比他大的数经过比赛后,只有第一个比他大的数最终险胜,他两比赛,之后i赢了i+1;所以遍历循环,对于第i个数,若与i+1的差大于k,则i之前包括i都不可能获胜,num=0,否则num++;第n个数由于和0比,比大于k,所以终点不用考虑。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
long long ori[maxn];
int main() {
long long k;
int n;
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> ori[i];
}
sort(ori, ori + n);
long long num = 0;
for (int i = 0; i < n; i++) {
if (ori[i + 1] - ori[i] <= k) {
num++;
}
else {
num = 0;
}
}
cout << num;
}
第六题 可重排列
题面http://oj.daimayuan.top/course/11/problem/113
next_permutation为什么是神!还是字典序,笑死。
#include<bits/stdc++.h>
using namespace std;
int ori[11];
int ori1[100];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> ori[i];
sum += ori[i];
}
int num = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < ori[i]; j++) {
ori1[num++] = i;
}
}
do {
for (int i = 0; i < sum; i++) {
printf("%d ", ori1[i]);
}
printf("\n");
} while (next_permutation(ori1, ori1 + sum));
}
第七题 进制转换
题面http://oj.daimayuan.top/problem/612
不会还有人不会进制转换吧,不会吧
#define _CRT_SECURE_NO_WARNINGS
#define ll long long
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, m;
scanf("%d%d", &n, &m);
ll sum=0;
for (int i = 0; i < n; i++) {
int t;
char a[100];
scanf("%d%s", &t, &a);
ll now = 0;
int len = strlen(a);
for (int j = len - 1; j >= 0; j--) {
if (a[j] >= '0' && a[j] <= '9') {
now += pow(t, len - j - 1) * (a[j] - '0');
}
else if (a[j] >= 'a' && a[j] <= 'z') {
now += pow(t, len - j - 1) * (a[j] - 'a' + 36);
}
else {
now += pow(t, len - j - 1) * (a[j] - 'A' + 10);
}
}
sum += now;
}
string ans;
while (sum > 0) {
int temp = sum % m;
if (temp > 35) {
ans += 'a' + temp-36;
}
else if(temp>9) {
ans += 'A' + temp - 10;
}
else {
ans += '0' + temp;
}
sum /= m;
}
reverse(ans.begin(), ans.end());
cout << ans;
}
第八题 循环子串
题面http://oj.daimayuan.top/problem/552
首先是对于循环的处理,我直接在原序列后面又加了一个原序列,ccca变成cccaccca这样,之后是判完全循环,按照题意对于任意子串都能在变化后的序列里找到子串,根据这个特征想到了回文串,只有变化后的序列里有一个长于n的,从开头开始的回文串,就一点是完全循环的,但不确定还有没有其他情况也满足,之后啊,试试发现过了,
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
scanf("%d", &t);
for (int i = 0; i < t; i++) {
int n;
char ori[1010];
char now[2020];
scanf("%d%s", &n, &ori);
copy(ori, ori + n, now);
copy(ori, ori + n, now + n);
bool no = 0;
for (int j = n; j < n + n; j++) {
no = 0;
for (int i = 0; i < j / 2; i++) {
if (now[i] != now[j - i - 1]) {
no = 1;
break;
}
}
if (!no) {
break;
}
}
if (no == 0) {
cout << "YES" << endl;
}
else {
cout << "NO" << endl;
}
}
}
第九题 饿饿 饭饭之暑假大狂欢
题面http://oj.daimayuan.top/course/11/problem/615
逻辑一地鸡毛结果过了,难以理解,首先,数量比当前判断的多的肯定没戏,之后数量小于等于当前的,若两者相同的数等于数量小的个数,如123和12,输出no,还有123,123 也是no,其他情况都是yes
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
int ori[110][110];
vector<int> num[110];
int num1[110];
bool cmp(vector<int> a, vector<int> b) {
return a[1] > b[1];
}
int main() {
int t;
scanf("%d", &t);
for (int i = 0; i < t; i++) {
int n;
scanf("%d", &n);
num[i].push_back(i);
int numi = 0;
for (int j = 0; j < n; j++) {
int k;
scanf("%d", &k);
if (ori[i][k] == 1) {
}
else {
ori[i][k] = 1;
numi++;
}
}
num[i].push_back(numi);
num1[i] = numi;
}
sort(num, num + t, cmp);
//cout << 1;
for (int i = 0; i < t; i++) {
for (int j = 0; j < t; j++) {
if (num[j][1] > num1[i]||num[j][0]==i)continue;
//比较有没有更快或相同的
//int numj = 0;
int tong = 0;
int j1 = num[j][0];
for (int k = 1; k <= 100; k++) {
if (ori[j1][k] + ori[i][k]==2) {
tong++;
}
//if (ori[j][k])numj++;
}
if (tong == num1[j1]) {
cout << "NO" << endl;
goto end1;
}
// else {
// cout << "YES" << endl;
// }
}
cout << "YES" << endl;
end1:;
}
}
第十题 RSA
题面http://oj.daimayuan.top/course/11/problem/617
本来想用刚学的6x±1来求质数的,结果发现第二个条件基本上绕不过找因数,因为kaa表示两者的因数和要么有出现两次及以上的,要么有因素是平方,所以,只能从头到尾遍历,之后用因素一个大于等于sqrt(n),一个小于等于来压范围
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//const int maxn = 1e6 + 10;
//int yinzi[maxn];
map<int, int>yinzi;
int main() {
long long a, b;
scanf("%lld%lld", &a, &b);
for (int i = 1; i <= (int)sqrt(a); i++) {
if (a % i == 0) {
yinzi[i]++;
yinzi[a / i]++;
}
}
for (int i = 1; i <= (int)sqrt(b); i++) {
if (b % i == 0) {
yinzi[i]++;
yinzi[b / i]++;
}
}
if (yinzi.size() == 3 && a != b) {
cout << "full credit" << endl;
return 0;
}
else {
map<int, int>::iterator it;
for (it = yinzi.begin(); it != yinzi.end(); it++) {
if (it->first == 1)continue;
int itsqrt = (int)sqrt(it->first);
if (it->second >= 2||itsqrt*itsqrt==it->first) {
cout << "no credit" << endl;
return 0;
}
}
cout << "partial credit" << endl;
return 0;
}
}