CF Round #677 div3 赛后总结

it2024-01-24  65

前言

运气真好AK了(╯‵□′)╯︵┻━┻

庆祝 E E E题没看懂题找规律过了(╯‵□′)╯︵┻━┻

A

题意: 差不多就是说,在 1 − 10000 1-10000 110000范围内,如果一个数字的每一位都是同一个数字,就叫无聊数字,然后会以 1 , 11 , 111 , 1111 , 2 , 22... 1,11,111,1111,2,22... 1,11,111,1111,2,22...这样的顺序排列,然后问无聊数字 x x x,以及其的排列前面的数字的位数之和是多少,例如: 11 11 11,前面有 1 1 1,然后位数之和为 2 + 1 = 3 2+1=3 2+1=3

暴力模拟啊。

#include<cstdio> #include<cstring> using namespace std; inline int solve(int x) { int type=x%10; int ans=(type-1)*10; int y=0; while(x) { x/=10; y++; ans+=y; } printf("%d\n",ans); } int main() { int T;scanf("%d",&T); for(int i=1;i<=T;i++) { int x;scanf("%d",&x); solve(x); } return 0; }

B

题意: 如果第 i i i个位置有书为 1 1 1,没书为 0 0 0。 对于书架上连续的一段书 [ l , r ] [l,r] [l,r],如果 r + 1 r+1 r+1的位置没有位置,则可以把 [ l , r ] [l,r] [l,r]的书右移到 [ l + 1 , r + 1 ] [l+1,r+1] [l+1,r+1],左移类似。 然后问把所有书变成连续的一段最小需要多少移动次数。

做法: 每次最多消掉一个间隔,不难发现,答案就是相邻每段书之间的间隔和。

#include<cstdio> #include<cstring> #define N 60 using namespace std; int a[N],n; void solve() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); bool bk=0;int ans=0,cnt=0; for(int i=1;i<=n;i++) { if(!a[i]) { if(bk)cnt++; } else { bk=1; ans+=cnt; cnt=0; } } printf("%d\n",ans); } int main() { int T;scanf("%d",&T); while(T--)solve(); return 0; }

C

题意: 差不多就是说,一条鱼 i i i,如果 a [ i − 1 ] < a [ i ] a[i-1]<a[i] a[i1]<a[i]或者 a [ i + 1 ] < a [ i ] a[i+1]<a[i] a[i+1]<a[i],就可以吃掉 i − 1 i-1 i1或者 i + 1 i+1 i+1的位置,然后 a [ i ] + + a[i]++ a[i]++,如果一条鱼,假定全局只有它能吃别的鱼,并且在最后它能吃掉所有的鱼,那么称其为优势鱼,然后问存不存在优势鱼,存在,随便输其中一条优势鱼的编号。

首先考虑权值最大的情况,那如果权值最大的鱼吃掉了一条鱼,那么所有的鱼它随便吃,因此只要存在一只权值最大的鱼且左右两边有一条鱼比它小,它就是优势鱼。

当时这样一定能判定没有优势鱼的情况吗?不难发现,这种做法找到的优势鱼一定是正确的,且如果找不到当且仅当所有的鱼权值相同,此时确实是没有优势鱼的,所以这种做法是正确的。

#include<cstdio> #include<cstring> #define N 310000 using namespace std; int a[N],n; inline int mymax(int x,int y){return x>y?x:y;} void solve() { scanf("%d",&n); int mmax=1; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); mmax=mymax(a[i],mmax); } a[0]=a[1];a[n+1]=a[n]; for(int i=1;i<=n;i++) { if(a[i]==mmax && (a[i-1]!=a[i] || a[i+1]!=a[i])) { printf("%d\n",i); return ; } } printf("-1\n"); return ; } int main() { int T;scanf("%d",&T); while(T--)solve(); return 0; }

同机房奆佬还想了一个类似栈的做法,不再赘述。虽然刚开始思路错了,但是后来发现改一下就能规避问题了。

D

题意: 每个点都有一个权值,然后让你用 n − 1 n-1 n1条边把 n n n个点连成一棵树,且要求边两端的点权值不能相同。

记录第一个点的颜色,找到第一个和第一个点不同颜色的点,记为 i d id id,如果一个点和第一个点不同颜色,则连向第一个点,如果和第一个点相同,则连向 i d id id

无解情况就是找不到 i d id id的情况。

#include<cstdio> #include<cstring> #define N 5100 using namespace std; int co[N],n; void solve() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&co[i]); } int shit=co[1]; int id=0; for(int i=2;i<=n;i++) { if(co[i]!=shit) { id=i; break; } } if(!id) { printf("NO\n"); return ; } printf("YES\n"); for(int i=2;i<=n;i++) { if(co[i]!=shit)printf("1 %d\n",i); else printf("%d %d\n",id,i); } } int main() { int T;scanf("%d",&T); while(T--)solve(); return 0; }

E

题意: 赛后才知道题意 首先,轮舞是什么? 篝火晚宴见过吧,手拉着手一个圈跳舞就是轮舞,现在把 n ( n 是 偶 数 ) n(n是偶数) n(n)个人(第 i i i个人编号为 i i i)等分成两个轮舞。 记为一种轮舞方案{ A , B A,B A,B}。 两个轮舞 X , Y X,Y X,Y相同,当且仅当选定某个点为开头时,从左往右形成的序列相同(也就是说 [ 1 , 2 , 3 ] [1,2,3] [1,2,3] [ 3 , 2 , 1 ] [3,2,1] [3,2,1]是不同的)。 轮舞方案{ A 1 , B 1 A_{1},B_1 A1,B1}和{ A 2 , B 2 A_{2},B_2 A2,B2}相同,当且仅当 A 1 = A 2 , B 1 = B 2 A_1=A_2,B_1=B_2 A1=A2,B1=B2或者 A 1 = B 2 , B 1 = A 2 A_1=B_2,B_1=A_2 A1=B2,B1=A2。 问有多少个不同的方案,答案保证在 l o n g long long l o n g long long范围内。

做法: 设 n = 2 m n=2m n=2m,先选 m m m个人组成左边的圈,即 C n m C_{n}^m Cnm,但是选出了 m m m个人,不同的圈排列个数是多少呢?我们不妨像题目中所说的,固定一个人就是在一号位置,其余人全排列,那么就是 ( n − 1 ) ! (n-1)! (n1)!,不难发现这样不会重复且可以找到所有的方案(显然正确因为每个人一定在圈上),然后就是 C n m ∗ ( m − 1 ) ! ∗ ( m − 1 ) ! C_{n}^{m}*(m-1)!*(m-1)! Cnm(m1)!(m1)!,但是发现一个圈方案左边右边都会被选上,所以还要除 2 2 2(其实有规避的方法,假设编号为 1 1 1的人一定被选上,那么就是 C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1)。

化一下式子: C n m ∗ ( m − 1 ) ! ∗ ( m − 1 ) ! 2 = n ! ∗ ( m − 1 ) ! ∗ ( m − 1 ) ! 2 ∗ m ! ∗ m ! = 2 n ! n 2 = 2 ( n − 1 ) ! n \frac{C_{n}^{m}*(m-1)!*(m-1)!}{2}=\frac{n!*(m-1)!*(m-1)!}{2*m!*m!}=\frac{2n!}{n^2}=\frac{2(n-1)!}{n} 2Cnm(m1)!(m1)!=2m!m!n!(m1)!(m1)!=n22n!=n2(n1)!

这个式子就简单了很多。这个就是我考场上发现的规律。

打开计算器,发现 19 ! 19! 19!不会爆 l o n g long long l o n g long long,直接乱搞。

#include<cstdio> #include<cstring> using namespace std; int main() { int n;scanf("%d",&n); if(n==2)printf("1\n"); else { long long ans=1; for(int i=1;i<n;i++)ans*=i; ans/=n/2; printf("%lld\n",ans); } return 0; }

F

一个 n ∗ m n*m nm的矩阵,每一行最多能选 m 2 \frac{m}{2} 2m个,然后要求选出来数的总和是 k k k的倍数,问总和的最大值是多少。

f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示每一行的第 i i i个数字,选了 j j j个,余数为 k k k的最大值,不难想到状态转移方程。

时间复杂度: O ( n 4 ) O(n^4) O(n4)

#include<cstdio> #include<cstring> #define N 90 using namespace std; inline int mymax(int x,int y){return x>y?x:y;} int a[N][N]; int f[2][N][N]; int n,m,K; void dp() { memset(f[0],-20,sizeof(f[0])); f[0][0][0]=0;int now=0,pre=1; int limit=m/2; for(int i=1;i<=n;i++) { now^=1;pre^=1; memset(f[now],-20,sizeof(f[now])); for(int j=0;j<=limit;j++) { for(int k=0;k<K;k++)f[now][0][k]=mymax(f[now][0][k],f[pre][j][k]); } for(int j=1;j<=m;j++) { now^=1;pre^=1; memset(f[now],-20,sizeof(f[now])); for(int k=1;k<=limit;k++) { for(int t=0;t<K;t++) { int shit=(t+a[i][j])%K; f[now][k][shit]=mymax(f[now][k][shit],f[pre][k-1][t]+a[i][j]); } } for(int k=0;k<=limit;k++) { for(int t=0;t<K;t++)f[now][k][t]=mymax(f[now][k][t],f[pre][k][t]); } } } now^=1;pre^=1; memset(f[now],-20,sizeof(f[now])); for(int j=0;j<=limit;j++) { for(int k=0;k<K;k++)f[now][0][k]=mymax(f[now][0][k],f[pre][j][k]); } printf("%d\n",f[now][0][0]); } int main() { scanf("%d%d%d",&n,&m,&K); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++)scanf("%d",&a[i][j]); } dp(); return 0; }

G

题意: 一个 n n n个点 m m m条边的无向图,有 k k k个订单,每个订单是从 x x x跑到 y y y,定义每个订单的费用为 x x x y y y的最短路,总费用为每个订单的费用和。

你最多可以让一条边变成 0 0 0,然后问你最小总费用是多少。

做法: 对每个点跑一遍最短路,因为是稀疏图, S P F A SPFA SPFA很快。为了不被hack,后面又打了一个Dij的版本

肯定用掉机会比不用最小费用更小一点(最多不变)。

枚举是哪条边变成了 0 0 0,对于订单 x − > y x->y x>y,有两种操作,一种走原来的路,一种为了走 0 0 0边而强行改变路线(可能没改),至于怎么让 x − > y x->y x>y强行走一条边,大家也是懂的啦,直接把 x x x走到边的一端的费用再加上另一端走到 y y y的费用即可。

Dij时间复杂度: O ( n m log ⁡ m + m k ) O(nm\log{m}+mk) O(nmlogm+mk) SPFA:

#include<cstdio> #include<cstring> #define N 1100 #define M 2100 using namespace std; typedef long long LL; inline LL mymin(LL x,LL y){return x<y?x:y;} LL d[N][N]; int list[N],head,tail,n,m,k; bool v[N]; struct node { int y,next; LL c; }a[M];int len,last[N]; inline void ins(int x,int y,LL c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;} void SPFA(int st) { memset(d[st],20,sizeof(d[st]));d[st][st]=0; list[head=1]=st;tail=2;v[st]=1; while(head!=tail) { int x=list[head++];if(head==n+1)head=1; v[x]=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(d[st][x]+a[k].c<d[st][y]) { d[st][y]=d[st][x]+a[k].c; if(!v[y]) { v[y]=1; list[tail++]=y;if(tail==n+1)tail=1; } } } } } struct SHIT { int x,y; SHIT(int xx=0,int yy=0){x=xx;y=yy;} }zjj1[N],zjj2[N]; int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++) { int x,y;LL c;scanf("%d%d%lld",&x,&y,&c); ins(x,y,c);ins(y,x,c); zjj2[i]=SHIT(x,y); } for(int i=1;i<=k;i++) { int x,y; scanf("%d%d",&x,&y); zjj1[i]=SHIT(x,y); } for(int i=1;i<=n;i++)SPFA(i); LL ans=(LL)99999999999999; for(int i=1;i<=m;i++) { LL sum=0; int x=zjj2[i].x,y=zjj2[i].y; for(int j=1;j<=k;j++) { int ax=zjj1[j].x,ay=zjj1[j].y; sum+=mymin(mymin(d[ax][x]+d[y][ay],d[ax][y]+d[x][ay]),d[ax][ay]); } ans=mymin(ans,sum); } printf("%lld\n",ans); return 0; }

Dij:

#include<cstdio> #include<cstring> #include<queue> #define N 1100 #define M 2100 using namespace std; typedef long long LL; inline LL mymin(LL x,LL y){return x<y?x:y;} LL d[N][N]; int n,m,k; bool v[N]; struct node { int y,next; LL c; }a[M];int len,last[N]; inline void ins(int x,int y,LL c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;} priority_queue<pair<LL,int>,vector<pair<LL,int> >,greater<pair<LL,int> > > fuck; void SPFA(int st) { memset(d[st],20,sizeof(d[st]));d[st][st]=0; memset(v,0,sizeof(v)); while(!fuck.empty())fuck.pop(); fuck.push(make_pair(0,st)); for(int i=1;i<=n;i++) { pair<LL,int> zwq=fuck.top();fuck.pop(); int x=zwq.second; while(v[x]) { zwq=fuck.top();fuck.pop(); x=zwq.second; } v[x]=1; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(!v[y] && zwq.first+a[k].c<d[st][y]) { d[st][y]=zwq.first+a[k].c; fuck.push(make_pair(d[st][y],y)); } } } } struct SHIT { int x,y; SHIT(int xx=0,int yy=0){x=xx;y=yy;} }zjj1[N],zjj2[N]; int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++) { int x,y;LL c;scanf("%d%d%lld",&x,&y,&c); ins(x,y,c);ins(y,x,c); zjj2[i]=SHIT(x,y); } for(int i=1;i<=k;i++) { int x,y; scanf("%d%d",&x,&y); zjj1[i]=SHIT(x,y); } for(int i=1;i<=n;i++)SPFA(i); LL ans=(LL)99999999999999; for(int i=1;i<=m;i++) { LL sum=0; int x=zjj2[i].x,y=zjj2[i].y; for(int j=1;j<=k;j++) { int ax=zjj1[j].x,ay=zjj1[j].y; sum+=mymin(mymin(d[ax][x]+d[y][ay],d[ax][y]+d[x][ay]),d[ax][ay]); } ans=mymin(ans,sum); } printf("%lld\n",ans); return 0; }
最新回复(0)