原题连接:https://cn.vjudge.net/contest/298281
B - Element Swapping
ZOJ - 4101
题意:数组a通过交换一对值得到数组b,且已知
x=
∑
i
=
1
n
i
∗
a
[
i
]
\sum_{i=1}^ni*a[i]
∑i=1ni∗a[i]和y=
∑
i
=
1
n
i
∗
a
[
i
]
∗
a
[
i
]
\sum_{i=1}^ni*a[i]*a[i]
∑i=1ni∗a[i]∗a[i] 和交换后的数组b,
求原来被交换的可能对数。
题解:用数组b求出交换后的x1=
∑
i
=
1
n
i
∗
b
[
i
]
\sum_{i=1}^ni*b[i]
∑i=1ni∗b[i]和y1=
∑
i
=
1
n
i
∗
b
[
i
]
∗
b
[
i
]
\sum_{i=1}^ni*b[i]*b[i]
∑i=1ni∗b[i]∗b[i]
那么
(
y
−
y
1
)
/
(
x
−
x
1
)
(y-y1)/(x-x1)
(y−y1)/(x−x1)就是对应被交换的两个数
(
v
a
l
1
+
v
a
l
2
)
∗
k
(val1+val2)*k
(val1+val2)∗k,其中k为他们两个数下标的距离差值,详见代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
const int maxn=100010;
int n;
int a[maxn];
ll x,y,x2,y2;
int c[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d%lld%lld",&n,&x,&y);
x2=y2=0LL;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
x2+=(ll)(i+1)*(ll)a[i];
y2+=(ll)(i+1)*(ll)a[i]*(ll)a[i];
}
ll xx=x-x2,yy=y-y2;
if(!xx&&yy||!yy&&xx||(xx&&yy%xx!=0)){
printf("0\n");
continue;
}
if(xx<0&&yy>0||xx>0&&yy<0){
printf("0\n");
continue;
}
ll ans=0;
if(!xx&&!yy){
memset(c,0,sizeof(c));
for(int i=0;i<n;i++){
c[a[i]]++;
}
for(int i=1;i<=100000;i++){
if(c[i]>=2)
ans+=(ll)c[i]*(ll)(c[i]-1)/2;
}
printf("%lld\n",ans);
continue;
}
ll h=yy/xx;
for(int i=0;i<n;i++){
int a2=h-a[i];// a[i] a2
if(a[i]==a2) continue;//
int k=xx/(a[i]-a2);
if(k<0) continue;
if((i+k)<n&&a[i+k]==a2){
ans++;
}
}
printf("%lld\n",ans);
}
return 0;
}
C - Array in the Pocket
ZOJ - 4102
题解转载自:https://blog.csdn.net/kzn2683331518/article/details/89680687
题意:给出长度为n的数组a,让你构造出的数组b,满足所有的b[i] != a[i],
且b中每个数字出现次数于a中相同,且b的字典序最小。
题解:
首先肯定是按照每个数字进行大小排序了[pair(x,x要填的个数)]。
接着是计算出每一个x:[pair(x待填的个数+x在后面不能填的位置数,x)]
cnt[x] =x待填的个数. cc[x] = cnt[x] + x在后面不能填的位置
(不包括当前位置);那么无解的情况当且仅当满足最大的cc[x]?> n。
如果有解:
肯定是优先填字典序最小的数字,但是有些数字如果在此位置不填进去,
就会造成后面的无解。
//比如样例2 3 3 5 5 5 5 3,第一个位置必须填5,而不是3
①数字x如果在此位置必须填,会满足条件:cc[x] == n-i+1
n-i+1是后面的全部长度,恰好等于cc[x],那么这个位置必须填x了
由于n-i+1在填的过程中是递减的,所以我们可以直接按照cc[x]从大到小维护,
每次只使用max(cc[x])进行比较即可
②如果不存在必须在此位置填的数字,那么填一个字典序最小的,并且跟a[i]
不相等的就可以根据①②,用set维护两个二元组pair(x,cnt[x])和pair(cc[x],x),
贪心就可以了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
#define ll long long
const int maxn=100010;
int a[maxn],b[maxn];
int cnt[maxn],cc[maxn];
int n,m;
typedef pair<int,int> P;
//pair排序优先第一键值排序
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
set<P>s,s2;
for(int i=1;i<=n;i++){
cnt[i]=cc[i]=0;
}
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
cnt[a[i]]++;
cc[a[i]]+=2;
}
for(int i=1;i<=n;i++){
if(cnt[i]){
s.insert(P(cc[i],i));//待填+后不可填
s2.insert(P(i,cnt[i]));//待填
}
}
if((--s.end())->first>n){
puts("Impossible");
continue;
}
for(int i=1;i<=n;i++){
s.erase(P(cc[a[i]],a[i]));
s.insert(P(--cc[a[i]],a[i]));
P mx=*(--s.end());
//最大的【待填+后不可填】等于当前到结尾的全部空位,说明这个位置必须填x
if(mx.first==n-i+1){
b[i]=mx.second;
}else{//否则填字典序最小的,并且跟a[i]不相等的就可以
if(a[i]==(s2.begin())->first)
b[i]=(++s2.begin())->first;
else
b[i]=(s2.begin())->first;
}
s2.erase(P(b[i],cnt[b[i]]));
if(--cnt[b[i]]) s2.insert(P(b[i],cnt[b[i]]));
s.erase(P(cc[b[i]],b[i]));
if(--cc[b[i]]) s.insert(P(cc[b[i]],b[i]));
}
for(int i=1;i<=n;i++)
printf("%d%c",b[i],i==n?'\n':' ');
}
return 0;
}
LiXin大佬解法(权值线段树):
#include<bits/stdc++.h>
using namespace std;
int sum[400005];
struct cc
{
int val,num;
bool operator < (const cc& x) const
{
return val<x.val;
}
}a[100005];
int b[100005];
void build(int l,int r,int rt) // 权值线段树
{
if(l==r)
{
sum[rt]=a[l].num*2;
return ;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}
void update(int x,int l,int r,int rt)
{
if(l==r)
{
sum[rt]--;
return ;
}
int mid=(l+r)>>1;
if(x<=mid) update(x,l,mid,rt<<1);
else update(x,mid+1,r,rt<<1|1);
sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}
int query(int l,int r,int rt)
{
if(l==r)
{
return l;
}
int mid=(l+r)>>1;
if(sum[rt<<1]<sum[rt<<1|1]) return query(mid+1,r,rt<<1|1);
else return query(l,mid,rt<<1);
}
int main()
{
int n,m,i,j,k,ans,num,t;
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof a);
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&b[i]);
a[b[i]].val=b[i],a[b[i]].num++;
}
build(1,n,1);
sort(a+1,a+n+1);
int tot;
for(tot=1;tot<=n&&a[tot].num==0;tot++);
int r=tot+1;
int sym=1;
k=sum[1];
if(k>n)
{
sym=0;
printf("Impossible\n");
continue;
}
for(i=1;i<=n;i++)
{
if(b[i]==a[tot].val)
{
if(r>n)
{
sym=0;
printf("Impossible\n");
break;
}
else
{
update(b[i],1,n,1); // 位置-1
if(n-i<sum[1])
{
k=query(1,n,1);
if(a[r].val==k)
{
a[r].num--;
update(a[r].val,1,n,1);
b[i]=a[r].val;
if(a[r].num==0)
r++;
}
else
{
update(k,1,n,1);
int tt=lower_bound(a+1,a+n+1,cc{k,0})-a;
b[i]=k;
a[tt].num--;
}
if(n-i<sum[1])
{
sym=0;
printf("Impossible\n");
break;
}
}
else
{
a[r].num--;
update(a[r].val,1,n,1);
b[i]=a[r].val;
if(a[r].num==0)
r++;
}
}
}
else
{
update(b[i],1,n,1);
if(n-i<sum[1])
{
k=query(1,n,1);
if(a[tot].val==k)
{
b[i]=a[tot].val;
a[tot].num--;
update(a[tot].val,1,n,1);
if(a[tot].num==0)
tot=r,r++;
}
else
{
int tt=lower_bound(a+1,a+n+1,cc{k,0})-a;
a[tt].num--;
b[i]=k;
update(k,1,n,1);
}
if(n-i<sum[1])
{
sym=0;
printf("Impossible\n");
break;
}
}
else
{
b[i]=a[tot].val;
a[tot].num--;
update(a[tot].val,1,n,1);
if(a[tot].num==0)
tot=r,r++;
}
}
}
if(sym)
for(i=1;i<=n;i++)
printf(i==n?"%d\n":"%d ",b[i]);
}
return 0;
}
E - Sequence in the Pocket
ZOJ - 4104
题意:给定一组数,每次操作,可以把一个数抽出,放在数组开头位置
求最少操作数,使得原数组有序
题解:贪心,最多次数为n,在数组上的最大数向前面找,每找到下一个最大的数,
则这个数可以不用动,答案次数-1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Node{
int value;
int index;
};
int T;
int n;
Node a[100005],b[100005];
bool cmp(Node a,Node b){
if(a.value!=b.value)
return a.value<b.value;
return a.index<b.index;
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i].value);
a[i].index=i;
b[i].value=a[i].value;
b[i].index=i;
}
sort(b,b+n,cmp);
int ans=n;
int j=n-1;
for(int i=b[n-1].index;i>=0;i--){
if(a[i].value==b[j].value){
j--;
ans--;
}
}
printf("%d\n",ans);
}
return 0;
}
F - Abbreviation
ZOJ - 4105
单词缩写,把单词中的元音去掉,注意保留首字母
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
const int maxn=10010;
int n;
char s[maxn];
char ss[6]={'a','e','i','o','u','y'};
bool ok(char x)
{
for(int i=0;i<6;i++)
if(x==ss[i]) return false;
return true;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%s",s);
int n=strlen(s);
if(n) printf("%c",s[0]);
for(int i=1;i<n;i++){
if(ok(s[i])) printf("%c",s[i]);
}
printf("\n");
}
return 0;
}
G - Lucky 7 in the Pocket
ZOJ - 4106
找出第一个>=n的被7整除、不能被4整除的数
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
const int maxn=10010;
bool ok(int x)
{
return x%7==0&&x%4!=0;
}
int n;
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=n;;i++){
if(ok(i)){
printf("%d\n",i);
break;
}
}
}
return 0;
}
H - Singing Everywhere
ZOJ - 4107
对于一个数组,如果一个数大于前面的、又大于后面的数,就是crack
删掉一个数使得数组的crack数最少
暴力O(n)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
const int maxn=100010;
int n,a[maxn];
bool b[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
b[i]=0;//
}
int tot=0;
for(int i=1;i+1<n;i++){
if(a[i]>a[i-1]&&a[i]>a[i+1]){
tot++;
b[i]=1;//crack
}
}
int ans=tot;
for(int i=0;i<n;i++){
if(i==0){
if((i+2)<n&&a[i]<a[i+1]&&a[i+1]>a[i+2])
ans=min(ans,tot-1);
}
else if(i==n-1){
if((i-2>=0)&&a[i]<a[i-1]&&a[i-1]>a[i-2])
ans=min(ans,tot-1);
}
else{
if(b[i-1]&&b[i+1]){
if(a[i-1]!=a[i+1])
ans=min(ans,tot-1);
else
ans=tot-2;
}
else{
int tmp=tot;
if(b[i-1]&&a[i-1]<=a[i+1])
tmp--;//
if(!b[i-1]&&(i-2)>=0&&a[i-2]<a[i-1]&&a[i-1]>a[i+1])
tmp++;//
if(b[i+1]&&a[i+1]<=a[i-1])
tmp--;
if(!b[i+1]&&(i+2)<n&&a[i+1]>a[i+2]&&a[i+1]>a[i-1])
tmp++;
ans=min(ans,tmp);
}
}
}
printf("%d\n",ans);
}
return 0;
}
I - Fibonacci in the Pocket
ZOJ - 4108
求
∑
i
=
a
b
f
(
i
)
\sum_{i=a}^bf(i)
∑i=abf(i)的的奇偶性j,其中
f
(
i
)
f(i)
f(i)为斐波那契数
斐波那契数满足奇奇偶的循环节
对于a b 考虑(奇奇偶)*(奇奇偶)9种情况
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
const int maxn=10010;
int cal(char s[],int n)
{
int h=0;
for(int i=0;i<n;i++){
h=h*10+s[i]-'0';
h%=3;
}
return h;
}
int judge(int s1,int s2)
{
if(s1==1&&s2==1||s1==2&&s2!=1||s1==0&&s2==1)
return 1;
return 0;
}
char a[maxn],b[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%s%s",a,b);
int s1=0,s2=0;
int len1=strlen(a),len2=strlen(b);
s1=cal(a,len1);
s2=cal(b,len2);
printf("%d\n",judge(s1,s2));
}
return 0;
}
J - Welcome Party(并查集+优先队列)
ZOJ - 4109
题意:n个人,m对朋友,一个人进入party,如果没有朋友在现场,
则此人不开心,设计进party序列,使得不开心人数最少,
且序列按字典序最小
输出最少不开心人数,和进场队列
题解:优先队列+并查集,按连通块分,每个连通块只需要一人不开心即可
并查集合并过程让小的作为父结点
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
const int maxn=1000010;
vector<int> ve[maxn];
int f[maxn];
int n,m;
bool vis[maxn];
void init()
{
for(int i=0;i<=n;i++){
f[i]=i;ve[i].clear();
vis[i]=0;
}
}
int find1(int x)
{
return x==f[x]?x:f[x]=find1(f[x]);
}
void union1(int a,int b)
{
int fa=find1(a),fb=find1(b);
if(fa<fb)
f[fb]=fa;
if(fa>fb)
f[fa]=fb;
}
int tot,b[maxn];
void bfs()
{
priority_queue<int,vector<int>,greater<int> > q;
int ans=0;
for(int i=1;i<=n;i++)
if(i==f[i]){
ans++;
vis[i]=1;
q.push(i);
}
tot=0;
while(!q.empty()){
int u=q.top();
q.pop();
b[tot++]=u;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
if(vis[v]) continue;
vis[v]=1;
q.push(v);
}
}
printf("%d\n",ans);
for(int i=0;i<tot-1;i++)
printf("%d ",b[i]);
printf("%d\n",b[tot-1]);
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
init();
int a,b;
while(m--){
scanf("%d%d",&a,&b);
union1(a,b);
ve[a].push_back(b);
ve[b].push_back(a);
}
bfs();
}
return 0;
}
K - Strings in the Pocket(manacher)
ZOJ - 4110
题意:两个串s,t,多少对不同的(l,r),使得翻转s[l,r]后s等于t。
分析: s不等于t时,找出左右两端第一个不相等的位置a,b,首先判断翻转[a,b]后s是否等于t,接着往左右两端扫一遍判断字符是否相等。s等于t时,很容易想到就是s的回文串个数
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=2000010;
char s1[2*maxn],s2[2*maxn];
int n,p[2*maxn];
ll manacher(char str[])
{
int len=strlen(str),id=0,mx=0;
ll ans=0;
for(int i=len;i>=0;i--){
str[i*2+2]=str[i];
str[i*2+1]='#';
}
str[0]='*';str[2*len+2]='\0';
for(int i=2;str[i];i++){
p[i]=mx>i?min(p[2*id-i],mx-i):1;
while(str[i+p[i]]==str[i-p[i]])
p[i]++;
ans+=p[i]/2;//求所有回文串个数
if(i+p[i]>mx){
mx=i+p[i];
id=i;
}
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%s%s",s1,s2);
n=strlen(s1);
int l=0,r=n-1;
for(;l<n;l++) if(s1[l]!=s2[l]) break;
for(;r>=0;r--) if(s1[r]!=s2[r]) break;
if(l==r){
printf("0\n");continue;
}
if(l<n){
int ans=1;
for(int i=l;i<=r;i++)
if(s1[i]!=s2[l+r-i]){
ans=0;break;
}
if(ans){
l--;r++;
while(l>=0&&r<n&&s1[l]==s2[r]&&s1[r]==s2[l])
l--,r++,ans++;
}
printf("%d\n",ans);
}else{
printf("%lld\n",manacher(s1));
}
}
return 0;
}