本次A~D较为简单,E是一道很好的构造题,FG主要就是考察组合数和约数个数
A.小红的字符串切割
思路 :签到题
void solve()
{
string s;cin>>s;
int len=s.size();
cout<<s.substr(0,len/2)<<endl<<s.substr(len/2);
}
B.小红的数组分配
思路:题意就是要我们根据已有数据分成两个一模一样的数组,那么就要满足所有数都是偶数个,然后再将每个数输出一半的次数即可
void solve()
{
int n;cin>>n;
vector<int> v(2*n);
map<int,int> h;
for(auto &t:v)
{
cin>>t;
h[t]++;
}
int f=0;
for(auto t:h)
{
if(t.se&1)
{
f=1;
break;
}
}
if(f)
{
cout<<-1;
return ;
}
for(auto t:h)
{
for(int i=0;i<t.se/2;i++)
cout<<t.fi<<' ';
}
puts("");
for(auto t:h)
{
for(int i=0;i<t.se/2;i++)
cout<<t.fi<<' ';
}
}
C.小红关鸡
思路:排序+枚举+二分,根据题意要求最大概率,那么也就是用两个栅栏框住最多的鸡窝,那么也就是求在合法区间中鸡窝数的最大值,我们可以枚举左端点a[L],然后二分查找大于等于a[L]+k的鸡窝的位置pos,如果存在a[L]+k这个鸡窝,那么a[L]~a[L]+k这个区间的鸡窝个数为pos-L+1,如果不存在a[L]+k这个鸡窝,那么a[L]~a[L]+k这个区间的最右边的鸡窝的位置为pos-1,那么这个区间的鸡窝个数为pos-1-L+1
void solve()
{
int n,k;cin>>n>>k;
vector<int> v(n);
map<int,int> h;
for(auto &t:v)
{
cin>>t;
h[t]++;
}
sort(v.begin(),v.end());
int maxd=0;
for(int i=0;i<n;i++)
{
int x=v[i]+k;
int pos=lower_bound(v.begin()+i,v.end(),x)-v.begin();
if(h[x])
maxd=max(maxd,pos-i+1);
else maxd=max(maxd,pos-i);
}
double res=(maxd*1.0/n);
printf("%lf",res);//题目没说保留几位小数且误差不超过1e-4,所以输出所有小数位的结果误差远低于1e-4
}
D.小红的排列构造
思路:先用set<int> s将1~n全部存一遍,在输入数据时,如果s中存在这个数,则删掉这个数,否则说明这个数要么就是重复出现,要么就是不在1~n这个范围中,那么用一个数组存一下它的位置。输入完以后,s中保留的数就是需要将对应位置修改的数。
void solve()
{
int n;cin>>n;
map<int,int> h;
set<int> s;
for(int i=1;i<=n;i++) s.insert(i);
for(int i=1;i<=n;i++)
{
int t;cin>>t;
if(s.count(t))
s.erase(t);
else
h[i]=t;
}
cout<<h.size()<<endl;
for(auto t:h)
{
int pos=t.fi,x=t.se;
cout<<pos<<' '<<(*s.begin())<<endl;
s.erase(*s.begin());
}
}
E.小红的无向图构造
思路:这道题每条边的权值为1,如果要构建一棵二叉树,首先边至少为n-1,且每一层都要与上一层相连。那么我们可以做以下分析:
(1)m<n-1,肯定是无解的
(2)m>=n-1 ,用vector<int> g[N],存储每一层的节点,如果存在1~maxd(某个节点到1号节点的最短路径,也就是最大层数)中某一层为空,那么就无法构造出二叉树,无解。
(3)用vector<PII> ans 存储边,首先我们要构建一棵普通的二叉树,用第i层的所有节点连接第i-1层的第一个节点,ans.push_back({g[i-1][0],g[i][j]}),如果m>ans.size(),那么我们要考虑在同一层加边,或者第i层的所有节点与第i-1层除了g[i-1][0]的其他节点相连,如果这些操作都执行完了,仍不足m条边,则无解,否则遍历ans输出边
constexpr int N=1e5+5;
int a[N],n,m,maxd;
vector<int> g[N];
vector<PII> ans;
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
g[a[i]].eb(i);
maxd=max(maxd,a[i]);
}
if(m<n-1)
{
cout<<-1;
return ;
}
for(int i=1;i<=maxd;i++)
{
if(g[i].empty())
{
cout<<-1;
return ;
}
for(auto t:g[i])
ans.pb({g[i-1][0],t});
}
if(ans.size()<m)
{
//同一层的点相互连接
for(int i=1;i<=maxd;i++)
{
for(int j=1;j<g[i].size();j++)
{
for(int k=j-1;k>=0;k--)
{
ans.pb({g[i][j],g[i][k]});
if(ans.size()==m) break;
}
if(ans.size()==m) break;
}
if(ans.size()==m) break;
}
//当前层的点与上一层除了第一个点以外的其它点连接
if(ans.size()<m)
{
for(int i=2;i<=maxd;i++)
{
for(int j=1;j<g[i-1].size();j++)
{
for(int k=0;k<g[i].size();k++)
{
ans.pb({g[i-1][j],g[i][k]});
if(ans.size()==m) break;
}
if(ans.size()==m) break;
}
if(ans.size()==m) break;
}
}
}
if(ans.size()<m) cout<<-1;
else
{
for(auto t:ans)
cout<<t.fi<<' '<<t.se<<endl;
}
}
F.小红的子序列权值和(easy)
思路:任何一个数可以表示为p1^q1*p2^q2*...pk^qk,约数个数为(q1+1)*(q2+1)*...*(qk+1)。题目只有1~3这三种数,由于1比较特殊,所以我们只需枚举2和3的所有组合,ans+=C(sum2,i)*C(sum3,j)*(i+1)*(j+1)*temp,temp=pow(2,1的个数),因为每个1有选和不选两种情况,那么是否将1放到2和3这个组合种以及放多少就是temp种情况了
constexpr int N=1e5+5,mod=1e9+7;
int C[1005][1005];
map<int,int> h;
void solve()
{
int n;cin>>n;
for(int i=0;i<n;i++)
{
int x;cin>>x;
h[x]++;
}
int temp=1;
for(int i=0;i<h[1];i++) temp=temp*2%mod;
for(int i=0;i<=n;i++)
for(int j=0;j<=i;j++)
if(j==0||j==i) C[i][j]=1;
else C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
int ans=0,three=h[3],two=h[2];
for(int i=0;i<=two;i++)
for(int j=0;j<=three;j++)
{
ans+=C[two][i]*C[three][j]%mod*(i+1)%mod*(j+1)%mod*temp%mod;
ans%=mod;
}
cout<<(ans-1+mod)%mod;//还要减去一种子序列为空的情况
}
G.小红的子序列权值和(hard)
相较于easy版本n的数据范围改成了1e5
easy版本我们使用的方法时间复杂度为O(n*n),这里我们使用O(nlogn)做法,C(a,b)=a!/ ((a-b)!*b!),我们只需求1~n的阶乘,以及1~n的阶乘的逆元,根据easy版本我们得到公式C(cnt2,i)*(i+1)*C(cnt3,j)*(j+1)*2^cnt1,i属于[0,cnt2] , j属于[0,cnt3],i=0时,j属于[0,cnt3]时,ans1=C(cnt3,j)*(j+1)*2^cnt1,i=1时,j属于[0,cnt3]时,ans2=C(cnt2,1)*(1+1)*C(cnt3,j)*(j+1)*2^cnt1,
ans=ans1+ans2=(1+C(cnt2,1)*(1+1)) * (C(cnt3,j)*(j+1)) *2^cnt1,化简之后我们可以发现公式为 ,所以求和部分只需分别用一个循环就可以搞定了
constexpr int N=1e5+5,mod=1e9+7;
int fact[N],infact[N],n;
map<int,int> h;
void solve()
{
cin>>n;
auto qmi = [&](int a,int b) ->int{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
};
auto C = [](int a,int b) ->int{
if(a<b) return 0;
return fact[a]*infact[a-b]%mod*infact[b]%mod;
};
fact[0]=infact[0]=1;
for(int i=1;i<=n;i++)
{
fact[i]=fact[i-1]*i%mod;
infact[i]=infact[i-1]*qmi(i,mod-2)%mod;
}
for(int i=0;i<n;i++)
{
int x;cin>>x;
h[x]++;
}
int temp=1,c2=0,c3=0,cnt2=h[2],cnt3=h[3];
for(int i=0;i<h[1];i++) temp=temp*2%mod;
for(int i=0;i<=cnt2;i++)
c2=(c2+C(cnt2,i)*(i+1)%mod)%mod;
for(int i=0;i<=cnt3;i++)
c3=(c3+C(cnt3,i)*(i+1)%mod)%mod;
cout<<(c2*c3%mod*temp%mod-1+mod)%mod;//同样会有一种情况是空串
}