Educational Codeforces Round 78 (Rated for Div. 2)

场次链接
在这里插入图片描述
略炸。
A. Shuffle Hashing
题目链接
一个字符串,只有’a’到’z’,你可以任意调整其顺序,然后在其前面和后面加上若干字符,给t组数据,每组给2个字符串,问下面这个字符串是否由上面这个变化而来,是输出YES,否则输出NO
数据范围 1 ≤ t ≤ 100 1\leq t\leq 100 1t100, 1 ≤ ∣ s 1 ∣ , ∣ s 2 ∣ ≤ 100 1\leq |s_1|,|s_2|\leq 100 1s1,s2100
解 将第一个串中每个字符有多少个存起来,然后在第二个字符串中,枚举连续的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 1t100, 1 ≤ x , y ≤ 1 0 9 1\leq x,y\leq 10^9 1x,y109
解 可以将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 xy,并且与 x − y x-y xy的差为偶数,那么答案就是 n n n
否则 考虑加上下一位,若为奇数,则使得 s u m ( 1 − n ) − ( x − y ) sum(1-n)-(x-y) sum(1n)(xy)为偶数,回到上个情况,故答案为n+1,。若下一位为偶数,则需要加2个才能使得 s u m ( 1 − n ) − ( x − y ) sum(1-n)-(x-y) sum(1n)(xy)为偶数,故答案是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 1t1000, 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105, 1 ≤ a i ≤ 2 1\leq a_i\leq 2 1ai2
解 设草莓为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 1n5105, 1 ≤ l i , r i ≤ 2 n 1\leq l_i,r_i\leq 2n 1li,ri2n
解 首先要为一棵树的话,最后的边数要为 n − 1 n-1 n1,且不能有环。先将数据按 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 1n5105, 1 ≤ x i , y i ≤ 2 ∗ n 1\leq x_i,y_i\leq 2*n 1xi,yi2n
解 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 1n,m998244353 1 ≤ k ≤ 5000 1\leq k\leq 5000 1k5000
解 首先列出式子 ∑ 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} mni=1nCniik(m1)ni

考虑分子部分,根据第二类斯特林数可得 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=0nCniS(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(m1)nij=0min(i,k)CnjS(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=jnCniCij(m1)ni

将组合数拆开 进行化简得到 ∑ 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(ni)!(ij)!n!(m1)ni

那么就是要解决里面这个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(nj)!n!(ni)!(ij)!(nj)!(m1)ni

提出 n ! ( n − j ) ! {\frac{n!}{(n-j)!}} (nj)!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)(nj)!n!i=jn(ni)!(ij)!(nj)!(m1)ni

得到 ∑ 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)(nj)!n!i=jnCnjij(m1)ni

将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)(nj)!n!i=0njCnji(m1)nij

里面就是一个二项式定理求和 得到 ∑ 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)(nj)!n!mnj

把分母算上 得到 ∑ 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)(nj)!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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值