场次链接
略炸。
A. Shuffle Hashing
题目链接
一个字符串,只有’a’到’z’,你可以任意调整其顺序,然后在其前面和后面加上若干字符,给t组数据,每组给2个字符串,问下面这个字符串是否由上面这个变化而来,是输出YES,否则输出NO
数据范围
1
≤
t
≤
100
1\leq t\leq 100
1≤t≤100,
1
≤
∣
s
1
∣
,
∣
s
2
∣
≤
100
1\leq |s_1|,|s_2|\leq 100
1≤∣s1∣,∣s2∣≤100
解 将第一个串中每个字符有多少个存起来,然后在第二个字符串中,枚举连续的s1长度的字符串,判断每个字符数量是否相同即可。
复杂度
O
(
n
2
)
O(n^2)
O(n2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void work()
{
char a[105];
char b[105];
int c[26];
memset(c,0,sizeof(c));
int d[26];
memset(d,0,sizeof(d));
scanf("%s",a);
scanf("%s",b);
int la=strlen(a);
int lb=strlen(b);
for(int i=0;i<la;i++){
c[a[i]-'a']++;
}
for(int i=0;i+la<=lb;i++){
for(int j=0;j<la;j++){
d[b[i+j]-'a']++;
}
for(int j=0;j<26;j++){
if(c[j]!=d[j]){
break;
}
if(j==25){
printf("YES\n");
return;
}
}
memset(d,0,sizeof(d));
}
printf("NO\n");
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
scanf("%d",&T);
//cin>>T;
//T=1;
for(int i=1;i<=T;i++){
work();
}
}
B. A and B
题目链接
t组数据,每组给你x,y,你可以从1开始 依次将
1
,
2
,
3
,
4
,
…
…
1,2,3,4,……
1,2,3,4,……加到x,y其中一个上,问最少要几步才能使得
x
=
y
x=y
x=y
数据范围
1
≤
t
≤
100
1\leq t\leq 100
1≤t≤100,
1
≤
x
,
y
≤
1
0
9
1\leq x,y\leq 10^9
1≤x,y≤109
解 可以将x直接减掉y,然后从1开始 你可以选择减去他或者加上他,最少要几步才能得到0。首先考虑
s
u
m
(
1
,
n
)
sum(1,n)
sum(1,n),如果我把其中任意的一个数变成减,那么对和的影响是
2
,
4
,
6
,
…
…
,
2
n
2,4,6,……,2n
2,4,6,……,2n,所以 如果刚好在
n
n
n时
s
u
m
(
1
,
n
)
sum(1,n)
sum(1,n)大于
x
−
y
x-y
x−y,并且与
x
−
y
x-y
x−y的差为偶数,那么答案就是
n
n
n。
否则 考虑加上下一位,若为奇数,则使得
s
u
m
(
1
−
n
)
−
(
x
−
y
)
sum(1-n)-(x-y)
sum(1−n)−(x−y)为偶数,回到上个情况,故答案为n+1,。若下一位为偶数,则需要加2个才能使得
s
u
m
(
1
−
n
)
−
(
x
−
y
)
sum(1-n)-(x-y)
sum(1−n)−(x−y)为偶数,故答案是n+2。
复杂度
O
(
x
)
O(\sqrt{x})
O(x)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void work()
{
ll x,y;
scanf("%lld%lld",&x,&y);
if(x>y)swap(x,y);
ll z=y-x;
if(z==0){
printf("0\n");
return;
}
ll sum=0;
int cnt=0;
int tmp=0;
for(int i=1;;i++){
sum+=i;
cnt++;
if(sum>=z){
tmp=i;
break;
}
}
if((sum-z)%2==0){
printf("%d\n",cnt);
}else{
if(tmp%2==0){
printf("%d\n",cnt+1);
}else{
printf("%d\n",cnt+2);
}
}
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
scanf("%d",&T);
//cin>>T;
//T=1;
while(T--){
work();
}
}
C. Berry Jam
题目链接
t组数据,每组数据有2*n个罐子,每个罐子分为草莓和蓝莓,1为草莓,2为蓝莓,你可以从中间开始,拿走两边最靠近你的罐子,问你最少几次操作使得两种罐子数量相等。
数据范围
1
≤
t
≤
1000
1\leq t\leq 1000
1≤t≤1000,
1
≤
n
≤
1
0
5
1\leq n\leq 10^5
1≤n≤105,
1
≤
a
i
≤
2
1\leq a_i\leq 2
1≤ai≤2
解 设草莓为1,蓝莓为-1,用map存从n+1开始,获得每个值的最少时间。然后枚举左端点,log在map中查找需要在右边拿的最少时间,求最小值即可。
需要特判一下右边不拿的情况。
复杂度
O
(
n
log
)
O(n\log)
O(nlog)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int cnt=0;
int a[200005];
void work()
{
int n;
scanf("%d",&n);
int suma=0;
int sumb=0;
for(int i=1;i<=2*n;i++){
scanf("%d",&a[i]);
if(a[i]==1){
suma++;
}else{
sumb++;
}
}
int tmp=suma-sumb;
int cnt=0;
map<int,int>m;
for(int i=n+1;i<=2*n;i++){
if(a[i]==1){
cnt++;
}else{
cnt--;
}
if(m[cnt]==0){
m[cnt]=i-n;
}
//printf("%d ",cnt);
}
int ans=9999999;
if(tmp==0){
printf("0\n");return;
}
if(m[tmp]){
ans=m[tmp];
}
for(int i=n;i>=1;i--){
if(a[i]==1){
tmp--;
}else{
tmp++;
}
if(tmp==0){
ans=min(ans,n-i+1);
}
if(m[tmp]!=0){
ans=min(ans,n-i+1+m[tmp]);
}
}
printf("%d\n",ans);
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
scanf("%d",&T);
//cin>>T;
//T=1;
for(int i=1;i<=T;i++){
work();
}
}
D. Segment Tree
题目链接
给你n个线段,每个线段有
l
i
,
r
i
l_i,r_i
li,ri,如果两个线段有相交,但不完全包含,那么这两个线段之间有边,问最后组成的是否是一棵树。
数据范围
1
≤
n
≤
5
∗
1
0
5
1\leq n\leq 5*10^5
1≤n≤5∗105,
1
≤
l
i
,
r
i
≤
2
n
1\leq l_i,r_i\leq 2n
1≤li,ri≤2n
解 首先要为一棵树的话,最后的边数要为
n
−
1
n-1
n−1,且不能有环。先将数据按
l
l
l排序,然后将数据的
r
,
i
d
r,id
r,id依次插入set,插入之前用二分找到第一个能与该线段连上边的位置,然后遍历,将连上的点进行并查集,如果连到2个已经在同一个并查集中的点,那么就有环,返回NO,如果边数不为n-1,也输出NO,否则输出YES
复杂度
O
(
n
log
)
O(n\log)
O(nlog)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node
{
int l;
int r;
int id;
}num[500005];
bool cmp(node x,node y)
{
return x.l<y.l;
}
int f[500005];
int find(int x)
{
if(x!=f[x]){
return f[x]=find(f[x]);
}
return f[x];
}
void uni(int x,int y)
{
int x1=find(x);
int y1=find(y);
if(x1!=y1){
f[x1]=y1;
}
}
void work()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&num[i].l,&num[i].r);
num[i].id=i;
f[i]=i;
}
sort(num+1,num+n+1,cmp);
set<pair<int,int> >s;
set<pair<int,int> >::iterator it;
int sum=0;
for(int i=1;i<=n;i++){
if(sum>n-1){
printf("NO\n");return;
}
pair<int,int> k;
k.first=num[i].l;
k.second=num[i].id;
it=s.lower_bound(k);
while(it!=s.end()){
//printf("%d %d\n",(*it).first,num[i].r);
if((*it).first>num[i].r){
break;
}
// printf("???%d %d\n",(*it).second,num[i].id);
if(find(num[i].id)==find((*it).second)){
printf("NO\n");return;
}else{
uni(num[i].id,(*it).second);
sum++;
}
it++;
}
k.first=num[i].r;
s.insert(k);
}
//printf("%d\n",sum);
if(sum!=n-1){
printf("NO\n");
}else{
printf("YES\n");
}
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
//scanf("%d",&T);
//cin>>T;
T=1;
while(T--){
work();
}
}
E. Tests for problem D
与上题完全相反,给你一些边的关系,让你给出所有点的线段,使得满足这些关系。
数据范围
1
≤
n
≤
5
∗
1
0
5
1\leq n\leq 5*10^5
1≤n≤5∗105,
1
≤
x
i
,
y
i
≤
2
∗
n
1\leq x_i,y_i\leq 2*n
1≤xi,yi≤2∗n
解 vector建边,然后dfs,将点的左、右尽量靠近父节点的右边即可。
复杂度
O
(
n
)
O(n)
O(n)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>v[1000005];
int l[1000005];
int r[1000005];
int dfs(int past,int now)
{
//printf("%d %d %d\n",past,now,v[now].size());
int sz=1;
int tmp=r[now]-1;
for(int i=0;i<v[now].size();i++){
if(v[now][i]==past)continue;
l[v[now][i]]=tmp;
r[v[now][i]]=tmp+sz*2-1+v[v[now][i]].size();
sz+=dfs(now,v[now][i]);
tmp--;
}
return sz;
}
void work()
{
int n;
scanf("%d",&n);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
l[1]=1;
r[1]=v[1].size()+2;
dfs(0,1);
for(int i=1;i<=n;i++){
printf("%d %d\n",l[i],r[i]);
}
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
//scanf("%d",&T);
//cin>>T;
T=1;
while(T--){
work();
}
}
F. Cards
题目链接
m张牌中有一张王牌,有n次操作,每次操作将牌打乱顺序,拿出最顶上那张牌,记录后放回去。x为n次操作后拿出王牌的次数,问x^k的期望值。答案对998244353取模。
数据范围
1
≤
n
,
m
≤
998244353
1\leq n,m\leq 998244353
1≤n,m≤998244353,
1
≤
k
≤
5000
1\leq k\leq 5000
1≤k≤5000
解 首先列出式子
∑
i
=
1
n
C
n
i
∗
i
k
∗
(
m
−
1
)
n
−
i
m
n
\frac{\sum_{i=1}^{n}C_n^i*i^k*(m-1)^{n-i}}{m^n}
mn∑i=1nCni∗ik∗(m−1)n−i。
考虑分子部分,根据第二类斯特林数可得 n k = ∑ i = 0 n C n i ∗ S ( k , i ) ∗ i ! n^k=\sum_{i=0}^{n}C_n^i*S(k,i)*i! nk=∑i=0nCni∗S(k,i)∗i!。
代入分子可得, ∑ i = 1 n C n i ∗ ( m − 1 ) n − i ∑ j = 0 m i n ( i , k ) C n j ∗ S ( k , j ) ∗ j ! \sum_{i=1}^nC_n^i*(m-1)^{n-i}\sum_{j=0}^{min(i,k)}C_n^j*S(k,j)*j! ∑i=1nCni∗(m−1)n−i∑j=0min(i,k)Cnj∗S(k,j)∗j!
然后交换求和得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) ∗ j ! ∑ i = j n C n i ∗ C i j ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j)*j!\sum_{i=j}^nC_n^i*C_i^j*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)∗j!∑i=jnCni∗Cij∗(m−1)n−i
将组合数拆开 进行化简得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) ∑ i = j n n ! ( n − i ) ! ∗ ( i − j ) ! ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j)\sum_{i=j}^n{\frac{n!}{(n-i)!*(i-j)!}}*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)∑i=jn(n−i)!∗(i−j)!n!∗(m−1)n−i
那么就是要解决里面这个n的求和 进行一点点的变换 ∑ j = 0 m i n ( n , k ) S ( k , j ) ∑ i = j n n ! ( n − j ) ! ( n − j ) ! ( n − i ) ! ∗ ( i − j ) ! ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j)\sum_{i=j}^n{{\frac{n!}{(n-j)!}}\frac{(n-j)!}{(n-i)!*(i-j)!}}*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)∑i=jn(n−j)!n!(n−i)!∗(i−j)!(n−j)!∗(m−1)n−i
提出 n ! ( n − j ) ! {\frac{n!}{(n-j)!}} (n−j)!n!得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∑ i = j n ( n − j ) ! ( n − i ) ! ∗ ( i − j ) ! ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!}}\sum_{i=j}^n{\frac{(n-j)!}{(n-i)!*(i-j)!}}*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)(n−j)!n!∑i=jn(n−i)!∗(i−j)!(n−j)!∗(m−1)n−i
得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∑ i = j n C n − j i − j ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!}}\sum_{i=j}^nC_{n-j}^{i-j}*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)(n−j)!n!∑i=jnCn−ji−j∗(m−1)n−i
将i-j替换为i得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∑ i = 0 n − j C n − j i ∗ ( m − 1 ) n − i − j \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!}}\sum_{i=0}^{n-j}C_{n-j}^{i}*(m-1)^{n-i-j} ∑j=0min(n,k)S(k,j)(n−j)!n!∑i=0n−jCn−ji∗(m−1)n−i−j
里面就是一个二项式定理求和 得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∗ m n − j \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!}}*m^{n-j} ∑j=0min(n,k)S(k,j)(n−j)!n!∗mn−j
把分母算上 得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∗ m j \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!*m^j}} ∑j=0min(n,k)S(k,j)(n−j)!∗mjn!
复杂度 O ( k 2 ) O(k^2) O(k2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p=998244353;
ll dp[5005][5005];
ll qpow(ll a,ll n)
{
ll ret=1;
while(n){
if(n&1)ret=ret*a%p;
a=a*a%p;
n>>=1;
}
return ret;
}
void work()
{
ll n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
dp[0][0]=1;
for(int i=1;i<=k;i++){
dp[i][0]=0;
for(int j=1;j<=i;j++){
dp[i][j]=(dp[i-1][j]*j+dp[i-1][j-1])%p;
}
}
ll tmp=1;
ll invm=qpow(m,p-2);
ll ans=0;
for(int i=1;i<=k;i++){
tmp=tmp*(n+1-i)%p;
ans=(ans+tmp*qpow(invm,i)%p*dp[k][i]%p)%p;
}
printf("%lld\n",ans);
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
//scanf("%d",&T);
//cin>>T;
T=1;
while(T--){
work();
}
}