EDU 127
A
题意:
给定一个字符串仅含\(a,b\)的字符串,问能否用\(aa,aaa,bb,bbb\)拼成
题解:
只要连续的\(a\)或\(b\)长度大于等于\(2\),就可以拼成。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=3e5+10,mod=998244353,inf=2e9;
int n,m;
char s[N];
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
cin>>(s+1);
int n=strlen(s+1);
bool flag=1;
for(int l=1;l<=n;++l)
{
int r=l;
while(r<n&&s[r+1]==s[l]) ++r;
//cout<<l<<' '<<r<<"!!"<<endl;
if(l==r) flag=0;
l=r;
}
if(!flag) cout<<"NO\n";
else cout<<"YES\n";
}
}
}
signed main()
{
red::main();
return 0;
}
/*
1
1 2 3 4
*/
B
题意:
数轴上有\(n\)个点,坐标分别是\(a_i\),每个点可以向左,向右或不动,能否让所有点变成连续的一段。
题解:
设两点间距离为\(a_{i+1}-a_i\)
设\(num_i\)为相邻两个点距离为\(i\)的对数。
分类讨论:
\(1.\)相邻两个点距离超过\(3\),不行。
\(2.\)\(num_3\geq 2\),不行。
\(3.\)\(num_2+num_3\geq 3\),不行。
\(4.\)\(num_2>0\&\&num_3>0\),不行。
其他情况都可以。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=3e5+10,mod=998244353,inf=2e9;
int n,m;
int a[N];
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
cin>>n;
int num1=0,num2=0;
bool flag=0;
for(int i=1;i<=n;++i)
{
cin>>a[i];
if(i>1)
{
if(a[i]-a[i-1]<=1) continue;
if(a[i]-a[i-1]<=2) ++num1;
else if(a[i]-a[i-1]<=3) ++num2;
else flag=1;
}
}
if(flag||num2>=2||num1+num2>=3||(num1&&num2)) cout<<"NO\n";
else cout<<"YES\n";
}
}
}
signed main()
{
red::main();
return 0;
}
/*
5
3
1 3 5
3
1 2 3
4
1 2 3 7
1
1000000
3
2 5 6
*/
C
题意:
有\(n\)家商店,第\(i\)家商店以\(a_i\)的价格出售糖果,只卖一次。
每过一天,商店糖果价格涨一块钱。问每天拿\(x\)元,最多买几包糖。
题解:
先按价钱从小到大排序,然后每天二分答案。
但是其实不能一天一天的算。
可以发现有连续的很多天,二分的结果应该是一样的,直接跳过这些天。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=3e5+10,mod=998244353,inf=2e9;
int n,m,sum;
int a[N],s[N];
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
cin>>n>>m;
sum=0;
for(int i=1;i<=n;++i) cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;++i)
{
s[i]=s[i-1]+a[i];
}
for(int t=0;;)
{
int l=1,r=n;
while(l<=r)
{
if(s[mid]+mid*t<=m) l=mid+1;
else r=mid-1;
}
int k=l-1;
if(!k) break;
int d=m-(s[k]+k*t);
d=d/k+1;
sum+=k*d;
t+=d;
}
cout<<sum<<'\n';
}
}
}
signed main()
{
red::main();
return 0;
}
/*
1
1 2 3 4
*/
D
题意:
给定一个长度为\(n\)的数列\(a\)
把\(1\sim m\)插入到数列中的任意位置。
然后让数列相邻两个位置的数字大小的差值之和最小。
题解:
想象一下,如果有两个数字\(x,y(x<y)\)相邻,那么我就可以把\(x\sim y\)的所有数字插入到他两之间,而不额外产生任何代价。
所以只要判断一下,如果\(a\)中的最大数字没有\(m\)大,那就找个最合适的位置把\(m\)插入进去。
如果\(a\)中的最小数字没有\(1\)小,就再找个合适的位置把\(1\)插入进去。
然后算所有相邻位置的数字大小差值的和就可以了。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=5e5+10,mod=998244353,inf=2e9;
int n,m;
int a[N],c[N];
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
cin>>n>>m;
int sum=0;
int minn=inf,maxn=-inf;
for(int i=1;i<=n+1;++i) c[i]=0;
for(int i=1;i<=n;++i)
{
cin>>a[i];
minn=min(minn,a[i]),maxn=max(maxn,a[i]);
if(i>1)
{
int l=a[i-1],r=a[i];
if(l>r) swap(l,r);
sum+=r-l;
++c[l],--c[r+1];
}
}
for(int i=1;i<=n;++i)
{
c[i]+=c[i-1];
}
sum+=minn-1;
sum+=min(0ll,m-maxn);
cout<<sum<<'\n';
}
}
}
signed main()
{
red::main();
return 0;
}
/*
1
1 2 3 4
7 2 2 3 4 5 6 7 8 10
*/
E
题意:
给定一个有\(2^n-1(n\leq 18)\)个节点的完全二叉树。每个顶点上有一个字母\(A\)或\(B\)。
每个节点可以翻转左右儿子,可以产生多少种先序遍历不同的字符串。
如果左右儿子同构,那么\(dp[x]=dp[son[x][0]]*dp[son[x][1]]\)
否则,\(dp[x]=2*dp[son[x][0]]*dp[son[x][1]]\),因为可以决定谁是左子树。
怎么判断左右子树同构?暴力取字典序最小的情况。
一个节点最多作为\(18\)个节点的子节点,所以\(\sum_{x=1}^{2^n-1}str[x]\sim n*(2^{n-1})\)
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=1e6+10,mod=998244353,inf=2e9;
int n,m;
char s[N];
string str[N];
int dp[N];
inline void dfs(int now)
{
if(now*2>=(1<<n))
{
dp[now]=1;
str[now]=s[now];
//cout<<now<<' '<<s[now]<<' '<<dp[now]<<"!!"<<endl;
return;
}
dfs(now<<1);
dfs(now<<1|1);
string t1=str[now<<1],t2=str[now<<1|1];
int f1=dp[now<<1],f2=dp[now<<1|1];
if(t1==t2) dp[now]=f1*f2%mod;
else dp[now]=f1*f2%mod*2%mod;
if(t1<t2) str[now]=t1+s[now]+t2;
else str[now]=t2+s[now]+t1;
//cout<<now<<' '<<str[now]<<' '<<dp[now]<<"!!"<<endl;
}
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
cin>>(s+1);
dfs(1);
cout<<dp[1]<<'\n';
}
}
signed main()
{
red::main();
return 0;
}
/*
4
BAAAAAAAABBABAB
*/
F
题意:
计算有\(n\)个元素,\(k\)个逆序对,恰好\(x\)个位置满足\(p_i>p_{i+1}\)的排列的个数。
\(n\leq 998244352,1\leq k\leq 11,1\leq x\leq 11\)
题解:
根据\(dwt\)大爷说的,计数题要先划分等价类,对每种等价类单独计算种类数,再把各种等价类拼起来。
发现要求的逆序对数很少,下降对数也很少,说明大部分元素都在自己本来的位置待着,即\(p_i=i\)
相信可以发现(可以个屁),如果存在一个位置,满足\(max\{a_1……a_i\}=i\),那么\(i\)之前的部分和之后的部分将不会产生任何逆序对。
所以我们可以枚举一段,在这一段之中,除了结尾位置,所有位置的前缀最大值都不等于自己。
设\(f[s][t1][t2][i]\)表示已选数字集合为\(s\),有\(t1\)个逆序对,\(t2\)个下降对,结尾为\(i\)。
这里设置状态结尾为\(i\)是为了加入新数字时好更新逆序数和下降对数。
爆搜之后发现,当\(n>12\)时,\(t1>11\),所以只要\(dp\)到\(n\leq 12\)的情况。
最后做一个求和,\(s[n][i][j]\)是\(n\)个数字,\(i\)个逆序对,\(j\)个下降对的方案数
然后就可以做一个类似背包的\(dp\)
最坏情况是\(21,21,21\)这样的情况,所以最多有\(22\)个数字。
\(dp[a][b][c][d]\)表示\(a\)段,\(b\)个数字,逆序对数为\(c\),下降对数为\(d\)的方案数。
那么就可以枚举我们的最终序列是怎么得到的。
假设最终序列用了\(a\)段,\(b\)个数字,那么我们要把剩下的\(n-b\)个\(p_i=i\)的位置安排进去。
不妨看作一个模型:\(x_1+x_2+…+x_a+x_{a+1}=n-b\),其中\(x_i\geq 0\)
那么这种情况的方案数就是\(\dbinom{n-b+a}{a}\)
这个\(n\)很大,但是这个组合数可以递推得到。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=3e5+10,mod=998244353,inf=2e9;
int n=12,m;
int f[(1<<12)+10][12][12][13];
int s[13][12][12];
int dp[13][23][12][12];
int inv[55];
//已选集合为S,有t1个逆序对,t2个下降对,结尾为i
inline int fast(int x,int k)
{
int ret=1;
while(k)
{
if(k&1) ret=ret*x%mod;
x=x*x%mod;
k>>=1;
}
return ret;
}
inline int all(int n)
{
return (1LL<<n)-1;
}
inline void add(int &x,int y)
{
x=(x+y>=mod)?(x+y-mod):(x+y);
}
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
inv[0]=inv[1]=1;
for(int i=2;i<=22;++i) inv[i]=fast(i,mod-2);
for(n=2;n<=12;++n)
{
for(int s=0;s<=all(n);++s)
for(int j=0;j<=11;++j)
for(int k=0;k<=11;++k)
for(int l=0;l<=n;++l)
f[s][j][k][l]=0;
f[0][0][0][0]=1;
for(int o=1;o<=n;++o)
{
for(int s=0;s<=all(n);++s)
for(int c1=0;c1<=11;++c1)
for(int c2=0;c2<=11;++c2)
for(int lst=0;lst<=n;++lst)
if(f[s][c1][c2][lst]&&__builtin_popcount(s)+1==o)
{
int mx=0;
for(int i=1;i<=n;++i)
if((s>>(i-1))&1) mx=i;
for(int j=1;j<=n;++j) if(!((s>>(j-1))&1))
{
if(max(mx,j)>o||o==n)
{
int t1=c1+__builtin_popcount(s&(all(n)-all(j)));
int t2=c2+(lst>j);
if(t1<=11&&t2<=11) add(f[s|(1<<(j-1))][t1][t2][j],f[s][c1][c2][lst]);
}
}
}
}
for(int i=1;i<=11;++i)
for(int j=1;j<=11;++j)
for(int k=1;k<=n;++k)
add(s[n][i][j],f[all(n)][i][j][k]);
}
dp[0][0][0][0]=1;
for(int a=0;a<=11;++a)
for(int b=0;b<=22;++b)
for(int c=0;c<=11;++c)
for(int d=0;d<=11;++d)
if(dp[a][b][c][d])
for(n=2;n<=12;++n)
for(int i=1;c+i<=11;++i)
for(int j=1;d+j<=11;++j)
add(dp[a+1][b+n][c+i][d+j],dp[a][b][c][d]*s[n][i][j]%mod);
int T;cin>>T;
while(T--)
{
int x,y,z;cin>>x>>y>>z;
int ans=0;
for(int i=2;i<=min(22ll,x);++i)
{
int tmp=x-i+1;
for(int j=1;j<=11;++j)
{
add(ans,dp[j][i][y][z]*tmp%mod);
tmp=tmp*(x-i+j+1)%mod*inv[j+1]%mod;
//cout<<(x-i+j+1)<<"!!"<<endl;
}
//x-i个数,插入j个段
//x1+x2+x3+x_{j+1}=x-i+(j+1)
//C(x-i+(j+1)-1,j)
}
cout<<ans<<'\n';
}
}
}
signed main()
{
red::main();
return 0;
}
/*
1
1 2 3 4
*/