传送门:CF
本场因为C题赛时被题意绕晕了,导致狠狠的掉分(准确的来说是题意没有想错但是自己的脑子被绕晕了)
A题:A. LuoTianyi and the Palindrome String
考虑暴力进行循环,找到所有所有不同的字符对(不妨记为
[
l
,
r
]
[l,r]
[l,r]).那么对于这个字符区间来说,显然中间的所有字符都是可以选的(因为左右两端点已经是不同的了).并且因为这个字符对的存在,我们可以在这个字符串的基础上向左右延伸扩展.
对于每一个字符对的最大贡献取一个
m
a
x
max
max即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int main() {
int T=read();
while(T--) {
string s;cin>>s;
int ans=0;int l=0,r=0;
for(int i=0;i<s.length();i++) {
for(int j=s.length()-1;j>i;j--) {
if(s[i]!=s[j]) {
l=i;r=j;
ans=max(ans,2*min(l,(int)s.length()-r-1)+r-l+1);
}
}
if(ans==1) break;
}
if(ans==0) cout<<-1<<endl;
else {
cout<<ans<<endl;
}
}
return 0;
}
B题:B. LuoTianyi and the Table
不难发现应该将最小的那个值使用的最多,然后最大的值也使用的最多是最优的.
我们把玩一下题意之后就会发现
(
1
,
1
)
(1,1)
(1,1)的这个位置显然应该使用最大的数字(这对吗?不对.还可能使用最小的数字),所以此时就有两种情况了,因为篇幅原因,此处我只描述一下放最大值的情况.最小值的分析方法与之几乎相同
假设我们在(1,1)出放最大值.我们会发现
(
1
,
2
)
,
(
2
,
1
)
(1,2),(2,1)
(1,2),(2,1)的位置显然应该放最小值(
m
i
n
1
min1
min1)和较小值(
m
i
n
2
min2
min2).我们会发现显然只要最小值存在,那么我们的值就是
m
a
x
−
m
i
n
1
max-min1
max−min1,不然肯定就是
m
a
x
−
m
i
n
2
max-min2
max−min2.因为我们需要最大,所以我们需要后者的数量更小.诶此时我们会发现后者的数字和该行(或者该列)的个数有关.也就是后者的个数要么是
n
−
1
n-1
n−1,要么是
m
−
1
m-1
m−1.所以此时当
n
<
m
n<m
n<m时放在(2,1),反之放(1,2)即可.
所有的子矩阵的个数为n*m,
(
1
,
1
)
(1,1)
(1,1)位置自己占了一个位置,
m
a
x
−
m
i
n
2
max-min2
max−min2占了
n
−
1
n-1
n−1或者
m
−
1
m-1
m−1个矩阵,剩下来的都是
m
a
x
−
m
i
n
1
max-min1
max−min1.所以此时本题也就不难解决了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define int long long
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int b[maxn];
signed main() {
int T=read();
while(T--) {
int n=read(),m=read();
int maxx=-int_INF;
for(int i=1;i<=n*m;i++) {
b[i]=read();
}
sort(b+1,b+n*m+1);
maxx=b[n*m];
int minn1=b[1],minn2=b[2];
int Min=min(n,m);
cout<<max((n*m-Min)*(maxx-minn1)+(maxx-minn2)*(Min-1),
(n*m-Min)*(maxx-minn1)+(b[n*m-1]-minn1)*(Min-1))<<endl;
}
return 0;
}
C题:C. LuoTianyi and the Show
就是这道题,出题风格只能说很
c
n
cn
cn好吧,狠狠的让我把题意理解错了.
首先这道题的题意是我们可以任意的进行顺序安排,问存在一种可能的全坐下的最大人数,然后刚开始我当做"存不存在一种最大人数使得任意的排序都可以坐下".然后狠狠的被卡了.(当然这个错误题意也是可以做的,可以造出来当做新生赛的一道题了,就先这么定了)
还有就是这道题的安排方式是只能安排在最左端,最右端.当我在后半时期领悟到我之前看错题目之后然后又忘记了这个最字.导致又狠狠的
w
a
wa
wa了,最离谱的是还能过样例.
接下来讲一下这道题的解法吧.看明白题意之后那么这道题就很简单了.发现先放 − 1 , − 2 -1,-2 −1,−2和先放固定座位的人的情况分析是不一样的.
- 假设我们先放 − 1 -1 −1,此时我们因为刚开始没有人,所以此时我们放在了最右端,此时最右端就已经被放满了,所以此时我们就会发现无论如何 − 2 -2 −2都没办法放了.但是我们会发现此时我们可以将剩下的所有 − 1 -1 −1都插空到所有固定座位的人之间.这是为什么呢,因为我们此时的 − 1 -1 −1可以一直放,然后遇到第一个有固定座位的人,然后让这个固定座位的人坐下,然后继续在这个人的左边继续放 − 1 -1 −1.但是需要注意的是存不存在固定位置在m的人,这个需要特判一下
- 假设我们先放 − 2 -2 −2,此时的情况和 − 1 -1 −1是基本一样的.我们可以将 − 2 -2 −2放在所有的固定位置的人的位置之间,这样子是最优的
- 假设我们先放固定位置的人.那么此时我们会发现我们的 − 1 -1 −1只能放在这个位置的左边了, − 2 -2 −2只能放在这个位置的右边了.那么我们此时最优的方案就是先选第一个位置,然后在这个位置的左右不断的填补 − 1 , − 2 -1,-2 −1,−2,直到遇到下一个固定的位置,然后填上那一个位置,然后继续填补 − 1 , − 2 -1,-2 −1,−2.所以只要考虑枚举第一个固定的位置即可.
可能会有人会有一个小疑问,为什么我们遇到固定的位置一定要先填上固定的位置呢,不能继续填
−
1
,
−
2
-1,-2
−1,−2吗.现在来解决一下这个问题:
考虑到我们的填数的方向肯定是一个方向的(不妨假设在pos的位置我们开始往一个方向开始填数,不妨设填的数字为
−
1
-1
−1,也就是一直往左填),那么显然我们会发现用之前的填数方法
−
1
-1
−1是可以填在任意的左边位置的,那么我们可以填在固定的位置上的
−
1
-1
−1为什么不填在其他没有数的位置呢.因为我们的
−
1
-1
−1肯定是可以填在其他空着的位置上的.我们却将这个
−
1
-1
−1占用了其他数的位置.这显然是不优的.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];
map<int,int>mp;
int main() {
int T=read();
while(T--) {
int n=read(),m=read();mp.clear();
for(int i=1;i<=n;i++) {
a[i]=read();
}
vector<int>v;int v2=0,v3=0;
for(int i=1;i<=n;i++) {
if(mp[a[i]]&&a[i]>0) continue;
if(a[i]==-1) v2++;
else if(a[i]==-2) v3++;
else v.push_back(a[i]);
mp[a[i]]=1;
}
if(v.size()==0) {
cout<<min(max(v2,v3),m)<<endl;
continue;
}
int ans=0;
sort(v.begin(),v.end());
for(int i=1;i<v.size();i++) {
ans+=v[i]-v[i-1]-1;
}
int ans1=min(m,v2+(int)v.size()-(v.back()==m?1:0));
int ans2=min(m,v3+(int)v.size()-(v[0]==1?1:0));
int ans3=-int_INF;
for(int i=0;i<v.size();i++) {
int pos=i+1;
ans3=max(ans3,min(v[i]-(pos-1)-1,v2)+min(m-v[i]-((int)v.size()-pos),v3)+(int)v.size());
}
cout<<max(ans1,max(ans2,ans3))<<endl;
}
return 0;
}
D题:D1. LuoTianyi and the Floating Islands (Easy Version&Hard Version)
在此处我直接讲Hard Version了.代码也只给出D2的实现代码.
首先我们会发现,显然当
k
=
1
k=1
k=1的时候,我们每一次选择都只能得到1的贡献,所以最后的答案肯定是1
当
k
=
3
k=3
k=3时候,我们会发现诶,好像无论任何情况也只有1的贡献.合理猜想,当
k
=
2
n
+
1
k=2n+1
k=2n+1的时候,我们每一次的选择都只有1的贡献.下面来简单证明一下:
考虑现在有k个点(k为奇数).随机分布一下.那么显然此时我们至少可以找到一个点到所有点的距离和最小(记为g),原因是因为距离和必定存在一个最小值.那么所有k个点
a
1
,
a
2...
a
k
a1,a2...ak
a1,a2...ak必然通过一些边和这个g相连记为
e
1
,
e
2
,
e
3...
e
p
e1,e2,e3...ep
e1,e2,e3...ep.那么假设此时我们还存在一个点使得距离和最小,那么我们的这个点肯定是需要往这些边的方向进行移动的.假设现在往
E
E
E这条边的另一端进行移动.那么对于这条边来说,边的两端的点的个数肯定是不一样的.我们往一段移动必然会减少边的一端的距离和,增加另一端的距离和.并且此时的总距离和肯定是增加的,不然就与我们之前的最小值假设矛盾了.并且如果此时增加的话,说明如果我们继续向这个方向走一定是一直增加的,因为边的一段的点会越来越多.
所以我们得到一个结论就是当
k
&
1
k\&1
k&1时必然答案是1.
下面分析k为偶数的时候.
对于任意一种随机情况,我们假设此时的
u
u
u是一个好节点.那么对于这个情况来说,我们此时的好节点的个数就等于(1+u能往周围移动的点的个数)所以我们此时的问题就变成了如何计算u能移动多少次(因为每移动一次必然会增加一个点)
直接考虑比较困难,发现每次转移必然会通过一条边,所以我们考虑计算每一条边为这个移动所做出的贡献.我们发现只要一条边的两端随机分布的点的个数一样(也就等于k/2),那么这条边必然可以产生一个贡献.因为这条边的两个端点必然是好节点(因为每条链上的分布点的个数都小于k/2,所以无论往哪边移动,都不会减少距离和,并且在一直相等之后必然是增加的)[注意这个结论很重要].这个结论说明了只要这种边存在就能且必然会提供一个贡献.
反之我们可以证明无法扩散.为了保证方案数不重不漏,使用dfs进行遍历即可
最终的答案就是所有边能提供的贡献/总方案数+1.这个1是因为每一种方案必然会产生一个好节点.
所以本题代码也就不难给出了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
#define int long long
const int mod=1e9+7;
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
vector<int>edge[maxn];
int Size1[maxn];
ll qpow(ll a,ll b) {
ll ans=1;
while(b) {
if(b&1) ans=(ans*a)%mod;
b>>=1;
a=(a*a)%mod;
}
return ans;
}
void dfs1(int u,int per_u) {
Size1[u]=1;
for(int i=0;i<edge[u].size();i++) {
int v=edge[u][i];
if(v==per_u) continue;
dfs1(v,u);
Size1[u]+=Size1[v];
}
}
int ans=0;int n,k;
int fac[maxn],in_fac[maxn];
void init() {
fac[0]=in_fac[0]=1;
for(int i=1;i<=n;i++) {
fac[i]=fac[i-1]*i%mod;
in_fac[i]=in_fac[i-1]*qpow(i,mod-2)%mod;
}
}
int C(int a,int b) {
if(a>=b) return fac[a]*in_fac[b]%mod*in_fac[a-b]%mod;
else return 0;
}
void dfs3(int u,int per_u) {
int sum=0;
for(int i=0;i<edge[u].size();i++) {
int v=edge[u][i];
if(v==per_u) continue;
ans=(ans+C(Size1[v],k/2)*C(n-Size1[v],k/2)%mod)%mod;
dfs3(v,u);
}
}
signed main() {
n=read();k=read();
init();
for(int i=1;i<=n-1;i++) {
int u=read(),v=read();
edge[u].push_back(v);
edge[v].push_back(u);
}
if(k&1) {
cout<<1<<endl;
return 0;
}
dfs1(1,0);dfs3(1,0);
int sum=C(n,k);
cout<<(ans*qpow(sum,mod-2)%mod+1)%mod<<endl;
return 0;
}