Educational Codeforces Round 81

Link

VP 了这场,做出了 ABCDE。

B 题罚时四次是因为没想清楚就开冲,十分不冷静,甚至有一次忘记删调试就交了(但它能过第一个样例所以算罚时(哭))。

E 题想了个巨复杂的做法,差点没写完,但实际上有简单很多的做法,VP 的时候好像有一个瞬间这个做法一闪而过,不知道为啥没去往下想。

A

最终答案一定形如 7111.. 或者 111...,对 \(n\) 的奇偶性讨论一下即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cstdlib>
#include<ctime>
#include<ctime>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
void sol()
{
    int n=read();
    if(n==2)puts("1");
    else if(n==3)puts("7");
    else{
        if(n%2)putchar('7');
        else putchar('1');
        for(int i=1;i<n/2;i++)putchar(49);
        puts("");
    }
}
int main()
{
    int T=read();
    while(T--)sol();
}

B

\(0\) 的权值为 \(+1\)\(1\) 的权值为 \(-1\),那么就是问这个长度为无穷大的字符串有多少个权值前缀和为 \(x\)。开个桶记一下 \(s\) 前缀和的值的出现次数,那么一个值 \(v\) 能有贡献当且仅当 \(x\equiv v\pmod{sum}\)\(sum\) 表示 \(s\) 的权值总和。需要特判 \(sum=0\) 的情况。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cstdlib>
#include<ctime>
#include<ctime>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
const int N=2e5+10;
char s[N];
int cnt[N<<1];
void sol()
{
    int n=read(),x=read();
    scanf("%s",s+1);
    for(int i=0;i<=n*2;i++)cnt[i]=0;
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='1')sum--;
        else sum++;
        cnt[sum+n]++;
    }
    if(sum==0)
    {
        if(x+n>=0&&x+n<=2*n&&cnt[x+n])puts("-1");
        else puts("0");
    }
    else
    {
        int ans=0;
        if(!x)ans++;
        for(int i=-n;i<=n;i++)
        {
            if(x<=i&&sum<0&&(i%(-sum)-sum)%(-sum)==(x%(-sum)-sum)%(-sum))ans+=cnt[n+i];
            if(x>=i&&sum>0&&(i%sum+sum)%sum==(x%sum+sum)%sum)ans+=cnt[n+i];
        }
        printf("%d\n",ans);
    }
}
int main()
{
    int T=read();
    while(T--)sol();
}

C

\(s\) 建出子序列自动机,\(t\) 在子序列自动机上匹配,计算匹配轮数。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cstdlib>
#include<ctime>
#include<ctime>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
const int N=1e5+10;
int nxt[N][26],pos[26];
char s[N],t[N];
void sol()
{
    scanf("%s%s",s+1,t+1);
    int n=strlen(s+1),m=strlen(t+1);
    memset(pos,0,sizeof(pos));
    for(int i=n;i;i--)
    {
        for(int j=0;j<26;j++)
            nxt[i][j]=pos[j];
        pos[s[i]-'a']=i;
    }
    for(int j=0;j<26;j++)nxt[0][j]=pos[j];
    int ans=0;
    for(int i=1;i<=m;)
    {
        ans++;
        int pre=i;
        int j=0;
        while(1)
        {
            // printf("i=%d\n",i);
            if(nxt[j][t[i]-'a'])j=nxt[j][t[i]-'a'],i++;
            else {break;}
            if(i>m)break;
            // printf("j=%d\n",j);
        }
        if(pre==i)return puts("-1"),void();
    }
    printf("%d\n",ans);
}
int main()
{
    int T=read();
    while(T--)sol();
    return 0;
}

D

\(g=\gcd(a,m)\),那么 \(\gcd(\frac{a}{g}+\frac{x}{g},\frac{m}{g})=1\),那这大概就是一个和 \(\varphi\) 有关的东西,结合样例大胆猜想答案是 \(\varphi(\frac{m}{g})\),然后就过了。

证明并不困难,考虑到 \(0\le \frac xg<\frac mg\),那 \(\frac ag+\frac xg\) 一定会覆盖到模 \(\frac mg\) 意义下的所有值且每个值只覆盖一次。而根据辗转相除法 \(\gcd(\frac ag+\frac xg,\frac mg)=\gcd((\frac ag + \frac mg)\bmod \frac mg,\frac mg)\),也就是说上面要求的东西等价于 \(\sum_{i=0}^{\frac mg-1}[\gcd(i,\frac mg)=1]\),即 \(\varphi(\frac mg)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cstdlib>
#include<ctime>
#include<ctime>
using namespace std;
#define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int phi(int n) 
{
    int ans=n;
    for(int i=2;i*i<=n;i++) 
    {
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0) n/=i;
        }
    }
    if(n>1)ans=ans/n*(n-1);
    return ans;
}
void sol()
{
    int a=read(),m=read();
    int g=gcd(a,m);
    printf("%lld\n",phi(m/g));
}
signed main()
{
    int T=read();
    while(T--)sol();
}

E

枚举最终左边集合的最大值 \(x\),考虑分界点 \(l\) 在哪里时 \(p_i\) 会产生代价。设 \(pos_i\) 表示数值 \(i\) 在排列中的位置,不难发现当 \(pos_i>l\)\(i\le x\) 时或者 \(pos_i\le l\)\(i>x\) 时会产生 \(a_i\) 的代价,线段树维护这个东西(详见代码),\(x\) 增加时单点修改,每次都取个全局 \(\min\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cstdlib>
#include<ctime>
#include<ctime>
using namespace std;
#define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
const int N=2e5+10,inf=1e18;
struct sgt{
    struct seg{
        int l,r;
        int suml,sumr,lr;
    }t[N<<2];
    void pushup(int p)
    {
        int ls=p*2,rs=p*2+1;
        t[p].suml=t[ls].suml+t[rs].suml;
        t[p].sumr=t[ls].sumr+t[rs].sumr;
        t[p].lr=min(t[ls].lr+t[rs].sumr,t[rs].lr+t[ls].suml);
        t[p].lr=min(t[p].lr,t[ls].suml+t[rs].sumr);
    }
    void build(int p,int l,int r)
    {
        t[p].l=l,t[p].r=r;
        t[p].suml=0,t[p].sumr=0,t[p].lr=0;
        if(l==r)return t[p].lr=inf,void();
        int mid=(t[p].l+t[p].r)>>1;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
        pushup(p);
    }
    void modifyl(int p,int x,int d)
    {
        if(t[p].l==t[p].r)
        {
            t[p].suml=d;
            return;
        }
        int mid=(t[p].l+t[p].r)>>1;
        if(x<=mid)modifyl(p*2,x,d);
        else modifyl(p*2+1,x,d);
        pushup(p);
    }
    void modifyr(int p,int x,int d)
    {
        if(t[p].l==t[p].r)
        {
            t[p].sumr=d;
            return;
        }
        int mid=(t[p].l+t[p].r)>>1;
        if(x<=mid)modifyr(p*2,x,d);
        else modifyr(p*2+1,x,d);
        pushup(p);
    }
}T;
int a[N],p[N],pos[N];
signed main()
{
    int n=read();
    for(int i=1;i<=n;i++)p[i]=read(),pos[p[i]]=i;
    for(int i=1;i<=n;i++)a[i]=read();
    T.build(1,1,n);
    for(int i=1;i<=n;i++)T.modifyl(1,i,a[i]);
    int ans=1e18;
    ans=T.t[1].lr;
    for(int i=1;i<=n;i++)
    {
        T.modifyl(1,pos[i],0);
        T.modifyr(1,pos[i],a[pos[i]]);
        ans=min(ans,T.t[1].lr);
    }
    printf("%lld",ans);
}

F

基本上是从这里抄的

首先把期望转化为合法方案数除以总方案数。

把给定区间弄成左闭右开的,然后把端点离散化,设端点从小到大为 \(t_1,t_2,\cdots,t_m\),那么就有了 \(m-1\) 个区间 \([t_1,t_2),[t_2,t_3),\cdots,[t_{m-1},t_m)\)。设 \(f_{i,j}\) 表示考虑前 \(i\) 个数,第 \(i\) 个数放在第 \(j\) 个区间的方案数,枚举前面的区间放的数量 \(k\),有转移:

\[f_{i,j}=\sum_{k<i}\binom{t_{j+1}-t_j+i-k-1}{t_{j+1}-t_j-1}\sum_{s>j}f_{k,s} \]

其实就是个插板法。

答案就是:

\[\frac{\sum_{i=1}^{m-1} f_{n,i}}{\prod_{i=1}^n (r_i-l_i+1)} \]

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
void write(int n)
{
	if(n<0){putchar('-');n=-n;}
	if(n>9)write(n/10);
	putchar(n%10^48);
}
const int N=110,mod=998244353;
int qpow(int a,int n)
{
	int ans=1;
	while(n){
		if(n&1)ans=1ll*a*ans%mod;
		a=1ll*a*a%mod;
		n>>=1;
	}
	return ans;
}
int binom(int n,int m)
{
	if(n<m)return 0;
	int ans=1;
	for(int i=1;i<=m;i++)ans=1ll*ans*qpow(i,mod-2)%mod*(n-i+1)%mod;
	return ans;
}
int f[N][N];
int l[N],r[N],t[N];
int main()
{
	int n=read(),m=0;
//	for(int i=1;i<=n;i++)
//	{
//		for(int j=0;j<=i;j++)printf("%d ",binom(i,j));
//		puts("");
//	}
	for(int i=1;i<=n;i++)t[++m]=l[i]=read(),t[++m]=r[i]=read()+1;
//	for(int i=1;i<=n;i++)printf("[%d, %d)\n",l[i],r[i]);
	sort(t+1,t+m+1),m=unique(t+1,t+m+1)-t-1;
	for(int i=1;i<=n;i++)l[i]=lower_bound(t+1,t+m+1,l[i])-t,r[i]=lower_bound(t+1,t+m+1,r[i])-t;
//	for(int i=1;i<=m;i++)printf("t[%d]=%d\n",i,t[i]);
	f[0][m]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<m;j++)
		{
			for(int k=i-1;k>=0;k--)
			{
				if(l[k+1]>j||j>r[k+1]-1)break;
				int sum=0;
				for(int t=j+1;t<=m;t++)sum+=f[k][t],sum%=mod;
				f[i][j]+=binom(t[j+1]-t[j]+i-k-1,i-k)*1ll*sum%mod;
				f[i][j]%=mod;
			}
		}
	}
	int ans=0;
	for(int i=1;i<m;i++)ans+=f[n][i],ans%=mod;
	for(int i=1;i<=n;i++)ans=1ll*ans*qpow(t[r[i]]-t[l[i]],mod-2)%mod;
	printf("%d",ans);
	return 0;
}
posted @ 2022-01-21 16:19  zzt1208  阅读(69)  评论(3编辑  收藏  举报