1.排序(sort.in/.out/.cpp)
时间限制
1000ms
空间限制
256MB
【问题描述】
小Z有一个数字序列
a1,a2,⋯an
,长度为
n
,小Z只有一个操作:选定
如
a
序列为
现在给你一个序列 a ,问你是否可以通过一次操作把整个序列从小到大排好序(变成不降的)。
【输入格式】
第一行一个整数
【输出格式】
如果一次操作可以排好序,输出YES,否则输出NO。
【样例输入】
5
1 2 4 5 3
【样例输出】
YES
【数据规模和约定】
对于
30%
的数据,满足
n≤1000
对于
60%
的数据,满足
n≤105
对于
100%
的数据,满足
n≤106,1≤ai≤106
solution
- 求出最长不下降子序列的长度,小于 n−1 输出NO,否则输出YES,正确性显然
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
template<typename T>
void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
#define MAXN 1000010
int a[MAXN];
int d[MAXN];
int main() {
int n;
input(n);
for(int i=1;i<=n;i++)
input(a[i]);
int len=0;
d[++len]=a[1];
for(int i=2;i<=n;++i){
if(a[i]>=d[len]) d[++len]=a[i];
else *upper_bound(d+1,d+len+1,a[i])=a[i];
}
puts(len<n-1?"NO":"YES");
return 0;
}
2.同余方程组(mod.in/.out/.cpp)
时间限制
1000ms
空间限制
256MB
【问题描述】
求关于
x
的同余方程组
x%a2=b2
x%a3=b3
x%a4=b4
的大于等于
0
的最小正整数解。
【输入格式】
一行
【输出格式】
一行一个整数表示答案
【样例输入】
2 0 3 1 5 0 7 3
【样例输出】
10
【数据规模和约定】
对于
30
的数据,
ai≤40
, 保证
ai
均为素数。
对于
60
的数据,
1≤ai≤103
, 保证
ai
均互素。
对于
100
的数据,
0≤bi<ai,1≤ai≤103
solution
裸的中国剩余定理不互质做法
再展开感觉不太好,所以推荐这篇blog
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
template<typename T>
void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
#define MAXN 10
ll a[MAXN],m[MAXN];
ll gcd(ll a,ll b) {
return b?gcd(b,a%b):a;
}
void exgcd(ll a,ll b,ll &x,ll &y) {
if(!b) {
x=1,y=0;
return;
} else exgcd(b,a%b,y,x);
y-=(a/b)*x;
return;
}
ll inv(ll a, ll b) {
ll d=gcd(a,b);
if(d!= 1) return -1;
ll x,y;
exgcd(a,b,x,y);
return (x%b+b)%b;
}
bool merge(ll a1, ll m1, ll a2, ll m2, ll &a3, ll &m3) {
ll d=gcd(m1, m2);
ll c=a2-a1;
if(c%d) return false;
c=(c%m2+m2)%m2;
m1/=d,m2/=d,c/=d;
c*=inv(m1,m2);
c%=m2,c*=m1*d,c+=a1;
m3=m1*m2*d,a3=(c%m3+m3)%m3;
return true;
}
ll CRT() {
ll a1=a[1],m1=m[1];
for(int i=2;i<=4;i++) {
ll a2=a[i],m2=m[i];
ll m3,a3;
if(!merge(a1,m1,a2,m2,a3,m3))
return -1;
a1=a3,m1=m3;
}
return (a1%m1+m1)%m1;
}
int main() {
freopen("mod.in","r",stdin);
freopen("mod.out","w",stdout);
for(int i=1;i<=4;i++)
input(m[i]),input(a[i]);
cout<<CRT()<<endl;
fclose(stdin);
fclose(stdout);
return 0;
}
3.字符串(str.in/.out/.cpp)
时间限制
1000ms
空间限制
256MB
【问题描述】
如果把一个字符串从头到尾翻转后和原字符串相等,我们称之为回文串,比如“aabaa”、“())(”、“2017102”。
如果一个字符串存在两个出现过的字母出现的次数相等,我们称之为好的字符串。
现在给你一个由小写字母组成的字符串,问在这个字符串的所有连续子串中,好的回文串有多少个。(两个相同的回文串出现在不同位置算多次)。
【输入格式】
一行一个由小写字母组成的字符串。
【输出格式】
一行一个整数,表示答案。
【样例输入】
abcbaabcba
【样例输出】
6
【样例解释】
abcba s[1..5] a,b 出现次数相等
baab s[4..7] a,b 出现次数相等
cbaabc s[3..8] a,b 出现次数相等
bcbaabcb s[2..9] a,c 出现次数相等
abcbaabcba s[1..10] a,b 出现次数相等
abcba s[6..10] a,b 出现次数相等
【数据规模和约定】
len
表示字符串长度。
对于
30%
的数据,
len≤102
对于
60%
的数据,
len≤103
对于
100%
的数据,
1≤len≤104
solution
先想暴力,一个最直观的暴力就是找出回文串然后暴力统计每个字母出现的次数,然后枚举两个字符的出现次数是否相等
没错这个思路是可以A的,只不过需要一些优化
求回文串可以用 Manacher 优化到 O(n) ,不会 Manacher 的话,推荐这篇blog
然后我们可以利用回文串的性质
统计的时候只统计一半,计数的时候要加2,这个应该很显然吧
但是因为是只统计一半,回文串长度为奇数的时候要特别注意中间的字符
然后暴力 O(262) 判断是不是好的字符串就好啦
然后这个的总复杂度感觉比较有趣,所以我们来分析一下
Manacher 是 O(n) ,之后 n 变成了
2∗n+1 然后枚举每一个字符是 O(2∗n+1)
每个字符扩展出来的回文串有 x 个的话,判断是
O(262) 所以复杂度大约是 O(n+262∑|s|) ,其中 |s| 代表回文串的长度
以上是蒟蒻的理解可能不对,欢迎指错
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 10010
char s[MAXN<<1];
int p[MAXN<<1];
int n,m;
void Manacher() {
n=strlen(s+1);
for(int i=n;i>=1;--i)
s[i*2]=s[i];
for(int i=1;i<=n*2+1;i+=2)
s[i]='_';
int R,pos;
s[0]='#';
m=n*2+1;R=pos=0;
for(int i=1;i<=m;++i){
if(R<i){
p[i]=0;
for(;s[i+p[i]+1]==s[i-p[i]-1];++p[i]);
R=p[i]+i;
pos=i;
continue;
}
int j=pos*2-i;
if(i+p[j]<R) {
p[i]=p[j];
continue;
}
p[i]=R-i;
for(;s[i+p[i]+1]==s[i-p[i]-1];++p[i]);
R=p[i]+i;
pos=i;
}
return;
}
int sum[30];
int calc(int pos,int k) {
int ans=0;
memset(sum,0,sizeof(sum));
if(s[pos]>='a'&&s[pos]<='z') {
sum[s[pos]-'a'+1]++;
pos-=2;
} else pos--;
for(;k;pos-=2,k--) {
bool flag=false;
sum[s[pos]-'a'+1]+=2;
for(int i=1;i<=26;i++) {
if(!sum[i]) continue;
for(int j=i+1;j<=26;j++)
if(sum[i]==sum[j]) {
ans++;
flag=true;
break;
}
if(flag) break;
}
}
return ans;
}
int main(){
scanf("%s",s+1);
Manacher();
int ans=0;
for(int i=1;i<=m;++i)
if(p[i]!=0&&p[i]!=1)
ans+=calc(i,p[i]>>1);
printf("%d",ans);
return 0;
}