• Educational Codeforces Round 81 (Rated for Div. 2)


    A. Display The Number

    题解

    题目要求数越大越好,并且有暗示答案会爆longlong,
    于是我们就有如下策略:
    (n) 是偶数全填(1) ,因为(1) 只占两格,很值得
    (n) 是奇数的话先狂填(1) ,再还剩下(3) 个的时候填一个(7) 就好了

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    #define mem(a,b) memset(a,b,sizeof(a))
    typedef long long LL;
    typedef pair<int,int> PII;
    #define X first
    #define Y second
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    int w[10]={6,2,5,5,4,5,6,3,7,6};
    int T,n; 
    int main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();
    		if(n%2==0)
    			for(int i=1;i<=n/2;i++)printf("1");
    		else
    		{
    			printf("7");
    			for(int i=1;i<n/2;i++)printf("1");
    		}
    		printf("
    ");	
    	}
    	return 0;
    }
    

    B. Infinite Prefixes

    题解

    破题卡了好久,觉得语言难以描述怎么解

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    #define mem(a,b) memset(a,b,sizeof(a))
    typedef long long LL;
    typedef pair<int,int> PII;
    #define X first
    #define Y second
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    const int maxn=100010;
    int T,n,x,p0[maxn],p1[maxn],pre[maxn],MAX;
    char s[maxn];
    int main()
    {
    	T=read();
    	while(T--)
    	{
    		mem(p0,0);mem(p1,0);mem(pre,0);
    		n=read();x=read();
    		int cnt=0;
    		scanf("%s",s+1);
    		for(int i=1;i<=n;i++)p0[i]=p0[i-1]+(s[i]=='0'),p1[i]=p1[i-1]+(s[i]=='1'),pre[i]=p0[i]-p1[i];
    		if(x==0)cnt=1;
    		if(pre[n])
    		{
    			for(int i=1;i<=n;i++)if((x-pre[i])%pre[n]==0 && (x-pre[i])/pre[n]>=0)cnt++;
    		}
    		else
    		{
    			for(int i=0;i<=n;i++)if(x==pre[i])cnt++;
    		}
    		if(!pre[n] && cnt)printf("-1
    ");
    		else printf("%d
    ",cnt);
    	}
    	return 0;
    }
    

    C. Obtain The String

    题解

    特别像字符串匹配
    但是我没这么做,对模板串预处理,省得匹配串耽误时间。
    (pre[i]) 表示字母(i) 的最早出现位置,(suf[i][j]) 表示表示在第(i) 位字符后,字母(j) 最早出现位置,这个可以用(O(26n)) 预处理出来
    然后就直接在上面左右反复横跳就可以了

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    #define mem(a,b) memset(a,b,sizeof(a))
    typedef long long LL;
    typedef pair<int,int> PII;
    #define X first
    #define Y second
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    const int maxl=100010;
    int T;
    char s[maxl],t[maxl];
    int pre[30],suf[maxl][30];//pre(i)表示字符i最早出现位置 ,suf(i,j)表示在i字符后,字符j最早出现位置 
    int main()
    {
    	T=read();
    	while(T--)
    	{
    		mem(pre,42);mem(suf,42);
    		scanf("%s%s",s,t);
    		int l1=strlen(s),l2=strlen(t),cnt=0,ok=0;
    		for(int i=0;i<l1;i++)pre[s[i]-'a']=min(pre[s[i]-'a'],i);
    		for(int i=0;i<l1-1;i++)suf[i][s[i+1]-'a']=i+1;
    		for(int i=l1-1;i>=0;i--)
    			for(int j=0;j<26;j++)suf[i][j]=min(suf[i][j],suf[i+1][j]);
    		for(int i=0;i<l2;)
    		{
    			++cnt;
    			int pos=pre[t[i]-'a'];i++;
    			if(pos>=l1){ok=1;break;}
    			while(suf[pos][t[i]-'a']<l1)pos=suf[pos][t[i]-'a'],i++;
    		}
    		if(!ok)printf("%d
    ",cnt);
    		else printf("%d
    ",-1);
    	}
    	return 0;
    }
    

    D. Same GCDs

    题解

    (gcd(a,m)=x) ,有(a=k_1x,m=k_2x)
    又知道(gcd(a+b,m)=x),有(a+b=k_3x)
    现在问题就变成了,在闭区间内([k_1,k_1+k_2-1]) 内有多少个与(k_2) 互质
    拆成前缀问题 ,对(n) 分解质因数后对因子容斥原理即可
    有一道类似的题 这是链接
    一觉起来发现自己蠢死了,(gcd(a+x,m)=gcd((a+x)%m,m)) 所以 $sum^{k_1+k_2-1}_{k_1}[gcd(i,k2)=1] $ 直接就变成了(phi(k2))
    这样的话代码还能少些好几行

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    #define mem(a,b) memset(a,b,sizeof(a))
    typedef long long LL;
    typedef pair<int,int> PII;
    #define X first
    #define Y second
    inline LL read()
    {
    	LL x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    int T;
    LL a,m,x,k1,k2;
    LL gcd(LL a,LL b){return b==0 ? a : gcd(b,a%b);}
    #include<vector> 
    vector<LL> pme;
    LL count_prime(LL x,LL n){
        pme.clear();
        LL i,j;
        for(i=2;i*i<=n;i++)
            if(n%i==0){
                pme.push_back(i);
                while(n%i==0)n/=i;
            }
        if(n>1)pme.push_back(n);
        LL sum=0,value,cnt;
        for(i=1;i<(1<<pme.size());i++){
            value=1;
            cnt=0;
            for(j=0;j<pme.size();j++){
                if(i&(1<<j)){
                    value*=pme[j];
                    cnt++;
                }
            }
            if(cnt&1)
                sum+=x/value;
            else sum-=x/value;
        }
        return x-sum;
    }
    int main()
    {
    	T=(int)read();
    	while(T--)
    	{
    		a=read();m=read();
    		x=gcd(a,m);
    		k1=a/x;k2=m/x;
    		cout<<count_prime(k2+k1-1,k2)-count_prime(k1-1,k2)<<endl;
    	}
    	return 0;
    }
    

    事后补题

    E. Permutation Separation

    题解

    考试的时候没仔细想
    翻译题目条件,那个左右集合的限制的翻译过来就是存在一个值(val) ,左边都比(val) 小,右边(val) ,对于在(pos) 位置进行前后切割的方案,计算的答案是(sum^{pos}_{i=1}[p_i>val]a_i+sum^n_{i=pos+1}[p_i<val]a_i)
    看到了这个式子思路就很清晰了。我们选择枚举(val) ,当(val) 依次递增时,分析一波都有哪些位置的答案有了变化(此处省略**字),用一个支持区间加法,区间取min的线段树维护这些答案即可。
    复杂度(O(nlogn))

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    #define mem(a,b) memset(a,b,sizeof(a))
    typedef long long LL;
    typedef pair<int,int> PII;
    #define X first
    #define Y second
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    int w[10]={6,2,5,5,4,5,6,3,7,6};
    int T,n; 
    int main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();
    		if(n%2==0)
    			for(int i=1;i<=n/2;i++)printf("1");
    		else
    		{
    			printf("7");
    			for(int i=1;i<n/2;i++)printf("1");
    		}
    		printf("
    ");	
    	}
    	return 0;
    }
    

    F. Good Contest

    题解

    学长所谓的很朴素的思路[微笑脸]
    两种方式,一种是概率DP,还有一种是统计合法方案数最后除以总方案数
    前者做法好像标程是用多项式
    后者的话,高人们想出了更好的做法。
    一个十分正常的(dp)(dp[i][j]) 表示现在拍到(i) ,并且第(i) 场考试的人A题数为(j) 的合法方案数
    转移的话因为(j) 的取值太大,所以复杂度无法承受,尝试优化(j) 这个东西。
    (n) 个区间端点离散化([L_i,R_i+1)),这样就把整个数轴划分成了若干个不可分的区间线段,这样就把(j) 由具体数值转化为了区间序号,复杂度直接就降了下来。
    继续设(dp[i][j]) 表示现在排到(i) ,且之前的考试全都安排在了(j) 以及(j) 之后的区间里的方案数。
    转移的话,(dp[i][j]) 可由(dp[k][l]) 转移而来,其中(j geq L_{k+1} , j<R_{k+1}) ,(l>j) 。意义是可以将(k+1~i) 这些场考试全安排在(j) 这个区间上,转移的时候需要乘以一个组合系数,这个系数的意思是表示在长为n的闭区间中,选k个数有重复且有序的方案,我的公式是(sum^{k}_{i=1} C^{i}_{n}C^{i-1}_{k-1}) ,组合意义是从(n) 个数中选(i) 个数,(k) 个数就由这(i) 种数组成 然后再插板 。但其实答案也可以简单归结为(C^k_{n+k-1}) 。这个组合意义我也不是很懂,据说是打表打出来的??
    另外为什么一定要从(l>j) 转移而来,因为我们会发现(l=j) 是无法转移的,但是这样丝毫不影响我们计算最终答案。
    整体复杂度(O(n^4)),这个(n=50) 给的可真好。
    至于区间为什么要选取左闭右开的话,我猜这应该是一种常用技巧吧,要不然如果全选成左闭右闭的话,会在端点处出现一些麻烦。

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    #define mem(a,b) memset(a,b,sizeof(a))
    typedef long long LL;
    typedef pair<int,int> PII;
    #define X first
    #define Y second
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    const int MOD=998244353,maxn=55; 
    int quickpow(int a,int N)
    {
    	int res=1,tmp=a;
    	while(N)
    	{
    		if(N&1)res=((LL)res*(LL)tmp)%MOD;
    		tmp=((LL)tmp*(LL)tmp)%MOD;
    		N>>=1;
    	}
    	return res;
    } 
    int Inv(int a){return quickpow(a,MOD-2);}
    int n,L[maxn],R[maxn],dp[maxn][maxn<<1],node[maxn<<1],inv[maxn],sum[maxn][maxn<<1],len,pro=1,ans;
    int C(int x,int y)
    {
    	int res=0,c=x,c2=1;
    	for(int i=1;i<=y;i++)
    	{
    		if(i!=1)c=(((LL)c*(LL)(x-i+1))%MOD*inv[i])%MOD,c2=(((LL)c2*(LL)(y-i+1))%MOD*inv[i-1])%MOD;
    		res=(res+((LL)c*(LL)c2)%MOD)%MOD;
    	}
    	return res;
    } 
    int main()
    {
    	n=read();
    	inv[1]=1;
        for(int i=2;i<=n;i++)inv[i]=((LL)(MOD-MOD/i)*(LL)inv[MOD%i])%MOD;
    	for(int i=1;i<=n;i++)L[i]=read(),R[i]=read()+1,pro=((LL)pro*(LL)(R[i]-L[i]))%MOD,node[++len]=L[i],node[++len]=R[i];
    	sort(node+1,node+len+1);
    	len=unique(node+1,node+len+1)-node-1;
    	for(int i=1;i<=n;i++)
    	{
    		L[i]=lower_bound(node+1,node+len+1,L[i])-node;
    		R[i]=lower_bound(node+1,node+len+1,R[i])-node;
    	}
    	for(int i=0;i<=len;i++)sum[0][i]=1;
    	for(int i=1;i<=n;i++)
    	{//dp[i][j]表示目前已经填了i个数,当前位置在区间j的方案数 
    		for(int j=L[i];j<R[i];j++)
    			for(int k=i-1;k>=0 && j>=L[k+1] && j<R[k+1];k--)//可以将k+1~i次安排在这个区间上
    					dp[i][j]=(dp[i][j]+((LL)C(node[j+1]-node[j],i-k)*(LL)sum[k][j+1])%MOD)%MOD;//C(i,j)表示在长为i的闭区间中,选j个数有重复且有序的方案 
    		for(int j=len;j>=0;j--)sum[i][j]=(sum[i][j+1]+dp[i][j])%MOD;
    	}
    	cout<<((LL)sum[n][0]*(LL)Inv(pro))%MOD<<endl; 
    	return 0;
    }
    

    废话

    脑子还是不好使,处理问题速度太慢啊
    补题效率堪忧[微笑脸]

  • 相关阅读:
    BigDecimal 和NumberFormat及 获取总页数的应用
    格式化小数点和百分号 DecimalFormatter
    Vue 项目开发
    js 对象补充
    Vue 实例成员
    Vue 指令
    Vue 介绍
    Vue
    request-html
    Python 中的经典类新式类
  • 原文地址:https://www.cnblogs.com/FYH-SSGSS/p/12241935.html
Copyright © 2020-2023  润新知