Educational Codeforces Round 64(ECR64)

Educational Codeforces Round 64

CodeForces 1156A

题意:1表明圆,2表明正三角形,3表明正方形。给一个只含1,2,3的数列a,ai+1内接在ai内,求总共有多少个交点。算法

交了好多遍才过。分类讨论一下内接的状况,而后注意到当正方形内接圆形再内接三角形时会有一个点重复,减去便可。优化

codespa

#include<cstdio> int n,fig[105],ans,flag,haha[5][5]; int main() { scanf("%d",&n); haha[1][2]=3;haha[1][3]=4; haha[2][1]=3;haha[2][3]=999999; haha[3][1]=4;haha[3][2]=999999; for(int i=1;i<=n;i++) { scanf("%d",&fig[i]); ans+=haha[fig[i-1]][fig[i]]; if(fig[i]==2&&fig[i-1]==1&&fig[i-2]==3)ans--; if(ans>=999999) { printf("Infinite\n"); return 0; } } printf("Finite\n"); printf("%d",ans); }

CodeForces 1156B

题意:给一个字符串,要求从新排列后不能有字母表中相邻的字母被放在一块儿(如“ab”与“ba”就不合法)。若能够作到就输出字符串,反之输出“No answer”。code

相同的字母能够缩成一个。发现只有当给的字母是两个或三个相邻的字母时是没法作到的(“ab”,“abc”),特判处理。对全部的字母排序后就能够乱搞了。blog

当总共有奇数个字母时,将a(1+n)/2拿出来放到答案中的第一个,剩下的就按a1,an,a2,an-1......这样来排。排序

(总共有3个字母时可能第一个或最后一个与中间的那个相邻,特判处理一下)队列

当总共有偶数个字母时,将a(1+n)/2+1放到答案中的第一个,a1放到答案中的第二个,an放到答案的第三个,a(1+n)/2放到答案的第四个,剩下的就按an-1,a2,an-2,a3....这样来排。游戏

(一开始还想用哈密顿回路来作)字符串

codestring

 

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int T,cha[26],tot,tim[26],ans[26]; char ch[105]; int main() { scanf("%d",&T); while(T--) { scanf("%s",ch+1); int len=strlen(ch+1); for(int i=1;i<=len;i++) { if(!tim[ch[i]-'a']) cha[++tot]=ch[i]-'a'; tim[ch[i]-'a']++; } sort(cha+1,cha+1+tot); if((tot==3&&cha[1]+1==cha[2]&&cha[2]+1==cha[3])||(tot==2&&cha[1]+1==cha[2])) printf("No answer\n"); else { if(tot%2==0) { int mid1=tot/2,mid2=mid1+1; ans[1]=cha[mid2];ans[2]=cha[1]; ans[3]=cha[tot];ans[4]=cha[mid1]; int l=4; for(int i=2;i<mid1;i++) ans[++l]=cha[tot-i+1],ans[++l]=cha[i]; } else { ans[1]=cha[tot/2+1]; if(tot!=3||(tot==3&&cha[1]+1!=cha[2]&&cha[2]+1!=cha[3])) for(int i=1;i<=tot/2;i++) ans[i*2]=cha[i],ans[i*2+1]=cha[tot-i+1]; else { if(cha[1]+1==cha[2]) ans[2]=cha[3],ans[3]=cha[1]; else ans[2]=cha[1],ans[3]=cha[3]; } } for(int i=1;i<=tot;i++) for(int j=1;j<=tim[ans[i]];j++) printf("%c",ans[i]+'a'); printf("\n"); } memset(cha,0,sizeof cha);tot=0; memset(ch,0,sizeof ch); memset(tim,0,sizeof tim); } return 0; }

 

CodeForces 1156C

题意:给一串数列a与数字z,若|ai-aj|>=z,则ai与aj能够匹配。一个数只能匹配一次,求最大匹配数。

本能地给数列排序后,发现新数列有这样一个性质:如有两个匹配之间跨越范围不相交,那么咱们可让它们变成相交的,这样不会影响它们对答案的贡献。但这个性质反过来不必定成立,因此咱们匹配时最好让它们相交。

把数列的中点取出,把中点右边的点当作匹配的右端点,左边的当作左端点,依次匹配就行了。这样作可让全部匹配跨越范围都通过中点,即在中点相交。

论证一下严谨性。若是有一边的点能造成不过中点的匹配,那么它确定能经过另外一边的某个点变为过中点的匹配(由于取的是中点,两边点的个数最多相差1,因此另外一边确定存在一个点能保证造成新的匹配)。

code

 

#include<cstdio> #include<algorithm> using namespace std; int n,z,num[200005],ans,vis[200005]; int main() { scanf("%d%d",&n,&z); for(int i=1;i<=n;i++) scanf("%d",&num[i]); sort(num+1,num+1+n); for(int i=(1+n)/2+1,j=1;i<=n;i++) { while(vis[j])j++; if(!vis[i]&&!vis[j]&&num[i]-num[j]>=z) vis[i]=vis[j]=1,ans++,j++; } printf("%d\n",ans); }

 CodeForces 1156D

题意:给一棵树,边的权值只有0和1。数对(x,y),x到y的路径上通过权值为0的边后就再也没有通过权值为1的边,求知足条件的数对的个数。

用并查集求出全部边权只含0和1的连通块,设每一个连通块的点的个数为siz。而后分类讨论,只含权值为1或0的边的路径,则每一个连通块的贡献为siz*(siz-1)。含权值为0和权值为1的边的路径,枚举路径上01转变的点,找到包含它的只含0的连通块i与只含1的连通块j,产生的贡献为(sizi-1)*(sizj-1)。

(不须要担忧两个联通块还有其它公共点,由于这是棵树)

看到zzwy大佬还有一种巧妙的算法,直接枚举1.0转变的点,找到包含它的只含0的连通块i与只含1的连通块j,产生的贡献为sizi*sizj-1,这样还同时枚举了边权只含0或1的状况,减去1是去掉本身到本身。

code

 

#include<cstdio> int ori[200005][5],n,tim[200005][2]; long long ans=0; int find(int x,int k){return !ori[x][k]?x:ori[x][k]=find(ori[x][k],k);} int main() { scanf("%d",&n); for(int i=1,a,b,c;i<=n-1;i++) { scanf("%d%d%d",&a,&b,&c); int t1=find(a,c),t2=find(b,c); if(t1!=t2) ori[t1][c]=t2; } for(int i=1;i<=n;i++) tim[find(i,1)][1]++,tim[find(i,0)][0]++; for(int i=1;i<=n;i++) ans+=1ll*(tim[find(i,1)][1])*(tim[find(i,0)][0])-1; printf("%lld\n",ans); }

 CodeForces 1156E

题意:给一个数列p,求全部的数对(l,r)的个数,知足pl+pr=maxri=lpi。

这题抄的题解和代码:

对于每一个数a,咱们能够用单调队列求出它左边第一个和右边第一个比它大的数al与ar,那么中间的这段区间(l,r)它就是最大值,以后就暴力枚举就行了。经过相似启发式合并的思想(反正我还没学)能够证实复杂度为nlogn。

code

 

#include<cstdio> #define maxn 200005 int q[maxn],be=1,en,n,p[maxn],pos[maxn],L[maxn],R[maxn]; long long ans; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&p[i]),pos[p[i]]=i;p[n+1]=200005;p[0]=200005; for(int i=0;i<=n+1;i++) {for(;be<=en&&p[q[en]]<p[i];en--);L[i]=q[en];q[++en]=i;} for(int i=n+1;i>=0;i--) {for(;be<=en&&p[q[en]]<p[i];en--);R[i]=q[en];q[++en]=i;} for(int i=1;i<=n;i++) { if(i-L[i]<R[i]-i) for(int j=L[i]+1;j<i;j++) { if(R[i]>pos[p[i]-p[j]]&&pos[p[i]-p[j]]>i)ans++;} else for(int j=R[i]-1;i<j;j--) { if(L[i]<pos[p[i]-p[j]]&&pos[p[i]-p[j]]<i)ans++;} } printf("%I64d\n",ans); }

 CodeForces 1156F

题意:给一堆牌,随便拿,若此次的值小于上次则失败,等于上次的值则成功,大于上次的值则继续游戏,求胜利的指望值。

第一道本身写的几率dpQAQ

首先对于任何一个不失败的状态,摸到牌造成的序列应该是不降低的,因此先排个序。咱们能够假设dp[i][j]表示第i张牌摸到了值为j的牌且游戏未结束的指望值。由于序列不降低且游戏还没有结束,那么值为j的牌就只用了一张。则dp[i][j]能够由dp[i+1][k](k>j)转移过来。首先,先预处理出每一个数字的个数cnt,而后就能够进行转移了。dp[i][j]的指望值应该是 (cntj-1)/n-i 加上(∑dp[i+1][k])/(n-i)(k>j),即下一次抽到j的指望值加上以后获胜的指望值,再用前缀和优化一下就行了。

code

 

#include<cstdio> #include<algorithm> #define maxn 5005 #define mod 998244353 using namespace std; int n,a[maxn],inv[maxn],num[maxn],cnt[maxn],f[maxn][maxn],flag=1; int main() { scanf("%d",&n); inv[1]=1;for(int i=2;i<=n;i++)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+1+n); for(int i=1;i<=n;cnt[a[i]]++,i++) if(!cnt[a[i]])num[++num[0]]=a[i]; sort(num+1,num+1+num[0]); for(int i=n;i>=1;i--) { long long sum=0; for(int k=1;k<=num[0];k++) sum=(sum+1ll*f[flag^1][k]*cnt[num[k]]%mod*inv[n-i]%mod)%mod; for(int j=1;j<=num[0];j++) { sum=(sum-1ll*f[flag^1][j]*cnt[num[j]]%mod*inv[n-i]%mod+mod)%mod; f[flag][j]=(1ll*(cnt[num[j]]-1)*inv[n-i]%mod+sum)%mod; } flag^=1; } int ans=0; for(int i=1;i<=num[0];i++) ans=(ans+1ll*f[flag^1][i]*cnt[num[i]]%mod*inv[n]%mod)%mod; printf("%d\n",ans); }

 CodeForces 1156G

恕在下不肖,真的不会。

相关文章
相关标签/搜索