B
题解
https://blog.csdn.net/Cassie_zkq/article/details/89607552
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(ll i = a; i < b; ++i)
#define FORE(a,b) for(ll i = a; i <= b; ++i)
typedef long long ll;
typedef pair<ll,ll> pii;
const ll maxn = 1e5+5;
vector<ll> v[maxn];
ll a[maxn],b[maxn];
ll gcd(ll a, ll b) {
return b == 0? a : gcd(b,a%b);
}
int main() {
ll T;
scanf("%lld", &T);
while(T--) {
ll n;
ll x,y;
for(int i = 1; i <= 100000; ++i)
v[i].clear();
scanf("%lld%lld%lld", &n, &x, &y);
for(ll i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
b[i] = a[i];
v[a[i]].push_back(i);
}
sort(b+1,b+1+n);
ll tot = unique(b+1,b+1+n)-b-1;
ll ox = 0, oy = 0;
for(ll i = 1; i <= n; ++i) {
ox += i*a[i];
oy += i*a[i]*a[i];
// cout << a[i] << endl;
}
// cout << ox <<" " << oy << endl;
ll ans = 0;
if(x-ox == 0) {
if(y != oy) {
puts("0");
continue;
}
for(ll i = 1; i <= tot; ++i) {
ll t = v[b[i]].size();
ans += t*(t-1)/2;
// v[b[i]].clear();
}
cout << ans << endl;
} else {
// cout << x-ox <<" "<< y-oy << endl;
if(gcd(x-ox, y-oy) != x-ox) {
puts("0");
}else {
ll t = (y-oy)/(x-ox);
// cout << t << endl;
for(ll i = 1; i<= n; ++i) {
ll ai = a[i], aj = t-ai;
if(aj == ai) continue;
ll j = i-(x-ox)/(aj-ai);
if(j <= i) continue;
// cout << ai <<" " << aj << " " << j << endl;
// if(ai == aj) continue;
if(a[j] == aj) ans++;
}
cout << ans << endl;
}
}
}
return 0;
}
C
题解
当一个数出现 > n/2 次,那肯定是Impossible。否则必定有解。
3 2 1 5 4 4 4 4 7
手动模拟这个例子,很快可以得出
3 2 1 5 4 4 4 4 7
1 3 2 4 5 7 x x x
我们发现此时只有4
能填,但是这个位置又不能填4
,那么就得找一个位置去填这个4
,很明显,在这个位置之前的我们按照贪心思路填的是最优的,没有到万不得已绝对不会去改变它,所以我们找此刻位置之后的能填4
的,序列变成
3 2 1 5 4 4 4 4 7
1 3 2 4 5 7 x x 4
此时都后面能填的都填了,没有办法了,已经到了万不得已的情况了,就只能动前面的了,贪心的想,肯定是动越靠后越优,并且此时上方和下方都不能是4
。所以我们可以取3 2
来填,并且这个3 2
应该按字典序最小的填(不按字典序也可以,往下看就知道了)。 序列变成
3 2 1 5 4 4 4 4 7
1 4 4 4 5 7 2 3 4
这种情况满足了条件,但并没有满足最小,此时对于4
这些位置我们并不是按贪心思路填下去的,所以应该再把4
的这些位置重排序一下,序列变成
3 2 1 5 4 4 4 4 7
1 4 4 4 2 3 5 7 4
这便是最优解。
代码
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 1e5+5;
int cnt[maxn], a[maxn], ans[maxn];
set<int> vis;
vector<int> va[maxn];
vector<int> vb[maxn];
int main() {
int T;
scanf("%d", &T);
while(T--) {
int n;
vis.clear();
scanf("%d", &n);
memset(cnt,0,sizeof cnt);
bool flag = true;
for(int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
cnt[a[i]]++;
if(cnt[a[i]] > n/2) flag = false;
vis.insert(a[i]);
ans[i] = 0;
}
if(!flag) {
puts("Impossible");
continue;
}
vector<int> v1,v2;
for(int i = 0; i < n; ++i) {
set<int> :: iterator it = vis.begin();
if(*it == a[i] && vis.size() == 1) {
for(int j = i+1; j < n; ++j) {
if(a[j] != a[i]) {
ans[j] = a[i];
cnt[a[i]]--;
}
}
for(int j = i; j < n; ++j) {
if(ans[j] == 0 && a[j] == a[i]) {
v1.push_back(j);
}
}
for(int j = i-1; j >= 0; --j) {
if(cnt[a[i]] == 0) break;
if(ans[j] != a[i] && a[i] != a[j]) {
v2.push_back(ans[j]);
ans[j] = a[i];
cnt[a[i]]--;
}
}
for(int j = 0; j < v1.size(); ++j) {
ans[v1[j]] = v2[j];
}
vis.erase(it);
v1.clear(); v2.clear();
break;
} else {
if(*it == a[i]) {
++it;
}
ans[i] = *it;
cnt[*it]--;
if(cnt[*it] == 0)
vis.erase(it);
}
}
for(int i = 0; i < n; ++i) {
va[a[i]].push_back(i);
vb[a[i]].push_back(ans[i]);
}
for(int i = 1; i <= n; ++i) {
if(va[i].size() == 0) continue;
sort(va[i].begin(), va[i].end());
sort(vb[i].begin(), vb[i].end());
for(int j = 0; j < va[i].size(); ++j)
ans[va[i][j]] = vb[i][j];
va[i].clear(); vb[i].clear();
}
for(int i = 0; i < n; ++i)
if(i == 0) printf("%d", ans[i]);
else printf(" %d", ans[i]);
puts("");
}
return 0;
}
E
zoj 4101
题解
从左到右遍历,找出不满足从最左端开始的非降序列的值中的最大值val,以及最先出现的位置pos,对于 < val的值都需要一次操作放到最前面,对于=val的,在pos后面出现的都需要一次操作放到最前面。
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 1e6+5;
int a[maxn],b[maxn];
int n;
int find(int x) {
int l = 1, r = n, ans = 0;
while(l <= r) {
int mid = (l+r)>>1;
if(a[mid] <= x) {
l = mid+1;
ans = mid;
} else
r = mid-1;
}
return ans;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
b[i] = a[i];
}
int pre = 0;
int mx = 0;
int pos = n;
for(int i = 1; i <= n; ++i) {
if(a[i] >= pre) {
pre = a[i];
} else {
if(mx < a[i]) {
mx = a[i];
pos = i;
}
}
}
sort(a+1,a+1+n);
int ct = 0;
int ans = 0;
ans = find(mx-1);
for(int i = pos; i <= n; ++i) {
if(mx == b[i]) {
ct++;
}
}
cout << ans + ct << endl;
}
return 0;
}
K
zoj 4110
题解
考虑可以经过一次翻转的情况
即s和t中只有一段[l,r]
是不相同的,并且这一段经过翻转之后要一模一样,否则就无解。
[l,r]
是必须翻转的,但是可以向两边扩散,条件就是
s
l
−
i
=
=
s
r
+
i
s_{l-i} == s_{r+i}
sl−i==sr+i。
分情况讨论:
- s == t,那么对于每个字符其最长的回文子串的半径就是对答案的贡献。采用马拉车可以O(n)算出。
- s != t,若可行,那么直接暴力从
l,r
向两边扩展。
代码
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=2000010;
char str[maxn];//原字符串
char str2[maxn];
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
//转换原始串
int INIT(char *st)
{
int i,len=strlen(st);
tmp[0]='@';//字符串开头增加一个特殊字符,防止越界
for(i=1;i<=2*len;i+=2)
{
tmp[i]='#';
tmp[i+1]=st[i/2];
}
tmp[2*len+1]='#';
tmp[2*len+2]='$';//字符串结尾加一个字符,防止越界
tmp[2*len+3]=0;
return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
ll MANACHER(char *st,int len)
{
int mx=0,po=0;
ll ans=0;//mx即为当前计算回文串最右边字符的最大值
for(int i=1;i<=len;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
else
Len[i]=1;//如果i>=mx,要从头开始匹配
while(st[i-Len[i]]==st[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
{
mx=Len[i]+i;
po=i;
}
ans += Len[i]/2;
}
return ans;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%s", str);
scanf("%s", str2);
if(strcmp(str,str2) == 0) {
int len = INIT(str);
printf("%lld\n", MANACHER(tmp,len));
} else {
int l = -1, r = -1;
int len = strlen(str);
for(int i = 0; i < len; ++i) {
if(str[i] != str2[i]) {
if(l == -1)
l = i;
r = i;
}
}
// cout << l << " " << r << endl;
bool flag = true;
for(int i = l; i <= r; ++i) {
// cout << str[i] << " "<< str2[r-i+l] << endl;
if(str[i] != str2[r-i+l]) {
flag = false;
break;
}
}
if(flag == false) {
puts("0");
continue;
}
int cnt = 1;
for(int i = l-1, j = r+1; i >= 0 && j < len && str[i] == str[j]; --i, ++j) cnt++;
printf("%d\n", cnt);
}
}
return 0;
}