题目
B.Element Swapping (数学+分类讨论)
链接: ZOJ-4101
题意:
数
组
a
通
过
交
换
一
对
数
字
,
得
到
了
b
数
组
。
数组a通过交换一对数字,得到了b数组。
数组a通过交换一对数字,得到了b数组。
给
出
如
下
的
两
个
值
x
=
∑
k
=
1
n
k
a
k
和
y
=
∑
k
=
1
n
k
a
k
2
和
b
数
组
.
给出如下的两个值x = \sum_{k=1}^{n}ka_k和y = \sum_{k=1}^{n}ka^2_k和b数组.
给出如下的两个值x=∑k=1nkak和y=∑k=1nkak2和b数组.
问
可
能
有
多
少
种
交
换
方
式
使
得
满
足
条
件
。
问可能有多少种交换方式使得满足条件。
问可能有多少种交换方式使得满足条件。
x
和
y
的
值
有
可
能
是
和
数
组
不
匹
配
的
,
这
样
的
情
况
时
输
出
0
。
x和y的值有可能是和数组不匹配的,这样的情况时输出0。
x和y的值有可能是和数组不匹配的,这样的情况时输出0。
题解:
已
知
x
=
∑
k
=
1
n
k
a
k
和
y
=
∑
k
=
1
n
k
a
k
2
,
已知x = \sum_{k=1}^{n}ka_k和y = \sum_{k=1}^{n}ka^2_k,
已知x=∑k=1nkak和y=∑k=1nkak2,
则
令
x
′
=
∑
k
=
1
n
k
b
k
和
y
′
=
∑
k
=
1
n
k
b
k
2
,
则令x' = \sum_{k=1}^{n}kb_k和y' = \sum_{k=1}^{n}kb^2_k,
则令x′=∑k=1nkbk和y′=∑k=1nkbk2,
假
设
a
i
,
a
j
交
换
成
为
了
b
i
,
b
j
,
即
a
i
=
b
j
,
a
j
=
b
i
。
假设a_i,a_j交换成为了b_i,b_j,即a_i=b_j,a_j=b_i。
假设ai,aj交换成为了bi,bj,即ai=bj,aj=bi。
有
X
=
x
′
−
x
=
i
b
i
+
j
b
j
−
i
a
i
−
j
a
j
=
(
b
i
−
b
j
)
∗
(
i
−
j
)
有X=x'-x=ib_i+jb_j-ia_i-ja_j=(b_i-b_j)*(i-j)
有X=x′−x=ibi+jbj−iai−jaj=(bi−bj)∗(i−j)
有
Y
=
y
′
−
y
=
i
b
i
2
+
j
b
j
2
−
i
a
i
2
−
j
a
j
2
=
(
b
i
2
−
b
j
2
)
∗
(
i
−
j
)
有Y=y'-y=ib_i^2+jb_j^2-ia_i^2-ja_j^2=(b_i^2-b_j^2)*(i-j)
有Y=y′−y=ibi2+jbj2−iai2−jaj2=(bi2−bj2)∗(i−j)
易
得
,
Y
X
=
(
b
i
2
−
b
j
2
)
∗
(
i
−
j
)
(
b
i
−
b
j
)
∗
(
i
−
j
)
=
b
i
+
b
j
=
C
(
常
数
)
易得,\frac{Y}{X}=\frac{(b_i^2-b_j^2)*(i-j)}{(b_i-b_j)*(i-j)}=b_i+b_j=C(常数)
易得,XY=(bi−bj)∗(i−j)(bi2−bj2)∗(i−j)=bi+bj=C(常数)
我
们
知
道
了
两
个
交
换
的
数
之
和
,
则
可
通
过
b
i
求
得
b
j
=
Y
X
−
b
i
,
枚
举
b
i
,
可
求
得
b
j
,
然
后
可
通
过
X
=
(
b
i
−
b
j
)
∗
(
i
−
j
)
得
到
j
的
下
标
i
−
X
b
i
−
b
j
,
只
需
验
证
求
得
的
下
标
j
是
否
满
足
b
j
即
可
。
我们知道了两个交换的数之和,则可通过b_i求得b_j=\frac{Y}{X}-b_i,枚举b_i,可求得b_j,然后可通过X=(b_i-b_j)*(i-j)得到j的下标i-\frac{X}{b_i-b_j},只需验证求得的下标j是否满足b_j即可。
我们知道了两个交换的数之和,则可通过bi求得bj=XY−bi,枚举bi,可求得bj,然后可通过X=(bi−bj)∗(i−j)得到j的下标i−bi−bjX,只需验证求得的下标j是否满足bj即可。
需
要
注
意
的
是
:
需要注意的是:
需要注意的是:
当
X
≠
0
,
Y
%
X
≠
0
时
,
x
,
y
不
匹
配
,
a
n
s
=
0
;
当X \not = 0,Y\%X \not =0时,x,y不匹配,ans=0;
当X=0,Y%X=0时,x,y不匹配,ans=0;
当
X
=
0
,
Y
≠
0
时
,
x
,
y
不
匹
配
,
a
n
s
=
0
;
当X = 0,Y \not = 0时,x,y不匹配,ans=0;
当X=0,Y=0时,x,y不匹配,ans=0;
当
X
=
Y
=
0
时
,
因
为
交
换
前
后
无
变
化
,
则
a
i
=
a
j
,
枚
举
每
个
数
出
现
的
次
数
n
u
m
i
,
a
n
s
=
∑
i
=
1
n
n
u
m
i
∗
(
n
u
m
i
−
1
)
2
当X=Y=0时,因为交换前后无变化,则a_i=a_j,枚举每个数出现的次数num_i,ans=\sum_{i=1}^{n}\frac{num_i*(num_i-1)}{2}
当X=Y=0时,因为交换前后无变化,则ai=aj,枚举每个数出现的次数numi,ans=∑i=1n2numi∗(numi−1)
代码:
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define ll long long
ll T,n,x,y,c;
ll suma,sumb,sum;
ll a[200500],b[200500];
unordered_map<ll,ll>dyy;
int main(){
cin>>T;
while(T--)
{
cin>>n>>x>>y;
suma=sumb=sum=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];suma+=a[i]*i;
b[i]=a[i]*a[i];sumb+=b[i]*i;
}
x=suma-x;//X
y=sumb-y;//Y
if(x!=0)
{
if(y%x)
{
cout<<0<<endl;
continue;
}
else c=y/x;//c=ai+aj
for(int i=1;i<n;i++)
{
ll aj=c-a[i];
if(a[i]-aj==0)continue;//检查除数a_i-a_j是否为0,因为X>0,为0则跳过
if(x%(a[i]-aj))continue;//防止x和y不匹配
int j=i-x/(a[i]-aj);//计算j的下标
if(j<=i||j>n)continue;//防止重复,对于每个i只查找之后的j
if(a[j]==aj)sum++;
}
}
else
{
if(y!=0)
{
cout<<0<<endl;
continue;
}
dyy.clear();
for(int i=1;i<=n;i++)
{
dyy[a[i]]++;
}
for(auto t:dyy)
sum+=t.second*(t.second-1)/2;
}
cout<<sum<<endl;
}
return 0;
}
E.Sequence in the Pocket (思维)
链接:ZOJ-4104
题意:
DreamGrid刚刚找到一个整数序列a1, a2, … , an 在他的右口袋里。当DreamGrid感到无聊时,他决定使用该序列。他可以执行任意次(包括零次)以下操作:选择一个元素并将其移到序列的开头。
使序列成为非递减序列所需的最少操作数是多少?
题解:
从最大值往前遍历。
遇到次大值,则次大值有序,继续往前寻找次次大值。
若遍历不到第
k
k
k大值,说明第
k
k
k大值位于
k
−
1
k-1
k−1值的后面,需要调至第一位,第k位之后的值也均需重新挪动至第一位才能保持序列非递减。
所以,
a
n
s
=
ans=
ans=无法被遍历到的第
k
k
k大值及之后数字的个数。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n,t,k;
ll a[100500],b[100500];
int main(){cout.tie(0);
cin>>t;
while(t--)
{
cin>>n;k=n;
for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i];
sort(b+1,b+1+n);
for(int i=n;i;i--)
{
if(a[i]==b[k])k--;
}
cout<<k<<endl;
}
return 0;
}
F.Abbreviation(签到)
链接:ZOJ-4105
题意:
去除从第二个字母开始的元音。
题解:
签到题
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
string s,str;
bool check(char x)
{
if(x=='str'||x=='e'||x=='i'||x=='y'||x=='o'||x=='u')return 0;
else return 1;
}
int main(){
cin>>n;
while(n--)
{
cin>>s;
str=s[0];
//cout<<s<<endl<<a<<endl;
for(int i=1;i<s.size();i++)
{
if(check(s[i]))str=str+s[i];
}
cout<<str<<endl;
}
return 0;
}
G.Lucky 7 in the Pocket(签到)
链接:ZOJ-4106
题意:
输入一个数字,找比他大的能被7整除不能被4整除的最小数字。
题解:
签到题
代码:
#include<bits/stdc++.h>
using namespace std;
int n,x,t;
bool check(int x)//判断是否是符合要求
{
if(x%7==0&&x%4!=0)return 1;
else return 0;
}
int main(){
cin>>n;
while(n--)
{
cin>>x;
if(check(x))cout<<x<<endl;
else
{
x=x+7-x%7;//找到最小的比x大的7的倍数
while(!check(x))x+=7;
cout<<x<<endl;
}
}
return 0;
}
H.Singing Everywhere (模拟)
链接:ZOJ-4107
题意:
将山峰定义为一个序列中大于左右两边的数,问删掉序列中的一个数后,序列中山峰数量最少的值。
题解:
每个点都删一遍,判断对于总体情况是否减少:
①当前点是山峰,删掉当前点时要注意两端是否会形成新的山峰?
a
n
s
ans
ans:
a
n
s
−
1
ans-1
ans−1
②当前点两边是山峰,如果两边高度相同则
a
n
s
−
2
ans-2
ans−2;
③端点旁有山峰
a
n
s
−
1
ans-1
ans−1
取最优情况即可
代码:
#include<bits/stdc++.h>
using namespace std;
int T,n;
int a[100500],b[100500];//b[i]记录点i是否为山峰
int ans,k;
int main(){
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++){cin>>a[i];b[i]=0;}
ans=0;
for(int i=2;i<n;i++)
if(a[i-1]<a[i]&&a[i]>a[i+1]){ans++;b[i]=1;}
k=0;
if(b[2]||b[n-1])k=1;
for(int i=2;i<n;i++)
{
if(b[i])
{
if(i==2||i==n-1){k=max(k,1);continue;}
else if((a[i-1]>a[i-2]&&a[i-1]>a[i+1])||(a[i+1]>a[i+2]&&a[i+1]>a[i-1]))continue;
k=max(k,1);
}
if(b[i-1]&&b[i+1])
{
if(a[i-1]==a[i+1])k=max(k,2);
else k=max(k,1);
}
}
cout<<ans-k<<endl;
}
}
I.Fibonacci in the Pocket (思维)
链接:ZOJ-4108
题意:
斐波那契数列,给定a,b,求
∑
i
=
a
b
f
(
i
)
\displaystyle\sum_{i=a}^{b}f(i)
i=a∑bf(i)的奇偶性。
题解:
斐波那契数列前六项奇偶性为1、1、0、1、1、0。
易得,奇偶性3位一个周期,即
f
(
x
)
f(x)
f(x)与
f
(
x
%
3
)
f(x\%3)
f(x%3)奇偶性相同。
欲求
∑
i
=
a
b
f
(
i
)
\displaystyle\sum_{i=a}^{b}f(i)
i=a∑bf(i)奇偶性,即求
f
(
a
)
f(a)
f(a)到
f
(
b
)
f(b)
f(b)间奇数的个数。同时每个循环节提供两个奇数,对结果无影响,因此可以去除
f
(
a
)
f(a)
f(a)到
f
(
b
)
f(b)
f(b)的循环节。题目即可转化成求
f
(
a
%
3
)
f(a\%3)
f(a%3)到
f
(
b
%
3
)
f(b\%3)
f(b%3)间奇数的个数。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,x,i,x1,x2;
int a[10];
string s1,s2;
int main(){
cin>>n;
a[0]=0;a[1]=1;a[2]=1;
a[3]=0;a[4]=1;a[5]=1;
while(n--)
{
cin>>s1>>s2;
x=x1=x2=0;
for(i=0;i<s1.size();i++)x1+=s1[i];
x1=x1%3;
for(i=0;i<s2.size();i++)x2+=s2[i];
x2=x2%3;
if(x2<x1)x2+=3;
for(i=x1;i<=x2;i++)x+=a[i];
x=x%2;
cout<<x<<endl;
}
return 0;
}
J.Welcome Party (并查集+优先队列)
链接:ZOJ-4109
题意:
给出
n
n
n个人,用
1
n
1 ~ n
1 n标号。
m
m
m个关系,每行
a
,
b
a,b
a,b表示
a
a
a和
b
b
b是朋友,朋友的关系不可传递。
如果一个人进场的时候场内没有自己认识的人则他会不开心,求最少的不开心人数,和一个合适的入场顺序使不开心的人数最少并且序列字典序最小。
题解:
先根据朋友关系跑一遍并查集,跑出的块数即为不开心的人数(每个朋友集体必有一个人进场的时候没遇到朋友)。
入场顺序:将每块中
i
d
id
id最小者放入优先队列,然后在让优先队列中最小
i
d
id
id者先进场,并将这位人的朋友们放入优先队列。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
vector<int>G[1000600];
int T,n,m,x,y,ans,cnt;
int par[1000600],vis[1000600],res[1000600];
void init(int n)
{
for(int i=1;i<=n;i++)
{
par[i]=i;
G[i].clear();
vis[i]=1;
}
}
int find(int x)
{
return par[x]==x?x:par[x]=find(par[x]);
}
void unite(int x,int y)
{
x=find(x);y=find(y);
if(x==y)return;
if(y<x)par[x]=par[y];
else par[y]=par[x];
}
void solve()
{
ans=cnt=0;
priority_queue<int,vector<int>,greater<int> >q;
for(int i=1;i<=n;i++)
{
if(par[i]==i)
{
q.push(i);
vis[i]=0;
ans++;
}
}
while(!q.empty())
{
int x=q.top();
res[++cnt]=x;
q.pop();
for(int i=0; i<G[x].size(); i++)
{
int y=G[x][i];
if(vis[y])
{
q.push(y);
vis[y]=0;
}
}
}
cout<<ans<<endl;
for(int i=1;i<cnt;i++)cout<<res[i]<<' ';cout<<res[cnt]<<endl;
}
int main(){
cin>>T;
while(T--)
{
cin>>n>>m;
init(n);
for(int i=1;i<=m;i++)
{
cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
unite(x,y);
}
solve();
}
return 0;
}
K.Strings in the Pocket (Manacher)
链接:ZOJ-4110
题意:
给
你
两
个
串
s
和
t
,
翻
转
一
次
s
串
的
一
个
区
间
[
l
,
r
]
,
问
有
多
少
对
l
,
r
使
得
翻
转
后
的
s
串
等
于
t
串
。
给你两个串s和t,翻转一次s串的一个区间[l,r],问有多少对l,r使得翻转后的s串等于t串。
给你两个串s和t,翻转一次s串的一个区间[l,r],问有多少对l,r使得翻转后的s串等于t串。
题解:
s
=
t
时
,
a
n
s
=
s
串
中
回
文
个
数
,
可
由
M
a
n
a
c
h
e
r
算
法
求
得
。
s=t时,ans=s串中回文个数,可由Manacher算法求得。
s=t时,ans=s串中回文个数,可由Manacher算法求得。
s
≠
t
时
,
找
出
最
左
最
右
的
不
等
坐
标
l
、
r
,
先
判
断
区
间
[
l
,
r
]
翻
转
后
s
、
t
串
是
否
相
等
,
不
相
等
则
无
解
;
s \not = t时,找出最左最右的不等坐标l、r,先判断区间[l,r]翻转后s、t串是否相等,不相等则无解;
s=t时,找出最左最右的不等坐标l、r,先判断区间[l,r]翻转后s、t串是否相等,不相等则无解;
相
等
的
话
就
将
l
、
r
向
外
扩
展
,
看
是
否
一
致
。
a
n
s
=
扩
展
次
数
+
1
相等的话就将l、r向外扩展,看是否一致。ans=扩展次数+1
相等的话就将l、r向外扩展,看是否一致。ans=扩展次数+1
代码:
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define ll long long
void manacher(char *str,ll *hw);
string s,t;
char str[5006000];
ll hw[5006000];
ll n,T,l,r,ans,f;
int main(){
cin>>T;
while(T--)
{
cin>>s>>t;
l=r=-1;
ans=0;
n=s.size();
for(int i=0;i<n;i++)
{
if(s[i]!=t[i])
{
l=i;
break;
}
str[i]=s[i];
}
if(l==-1)
{
manacher(str,hw);
for(int i=0;i<n;i++)ans+=hw[i]/2;
}
else
{
for(int i=n-1;i>=0;i--)
{
if(s[i]!=t[i])
{
r=i;
break;
}
}
f=1;
for(int i=l;i<=r;i++)
{
if(s[i]!=t[l+r-i])
{
f=0;
break;
}
}
if(f)
{
while(l>=0&&r<n)
{
if(s[l]!=t[r])break;
l--;r++;ans++;
}
}
}
cout<<ans<<endl;
}
return 0;
}
void change(char *str)
{
str[0]=str[1]='#';
for(int i=0; i<n; i++)
{
str[i*2+2]=s[i];
str[i*2+3]='#';
}
n=n*2+2;
str[n]=0;
}
void manacher(char *str,ll *hw)
{
change(str);
int maxright=0,mid;
for(int i=1; i<n; i++)
{
if(i<maxright)
hw[i]=min(hw[(mid<<1)-i],hw[mid]+mid-i);
else
hw[i]=1;
for(; str[i+hw[i]]==str[i-hw[i]]; ++hw[i]);
if(hw[i]+i>maxright)
{
maxright=hw[i]+i;
mid=i;
}
}
}