• 2022 年辽宁省大学生程序设计竞赛 个人题解



    title : 2022 年辽宁省大学生程序设计竞赛
    date : 2022-10-25
    tags : ACM,练习记录
    author : Linno


    2022 年辽宁省大学生程序设计竞赛

    题目链接:https://ac.nowcoder.com/acm/contest/43937

    进度:10/13

    质量比较差的场,后三题是错的,D题spj也是错的,其他nt题也多。

    A-伟大奋斗

    #include
    using namespace std;
    
    signed main(){
    	int n;
    	cin>>n;
    	int ans=n-(2022-73);
    	cout<<ans<<"\n";
    	return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    B-可莉的五子棋

    枚举每个点作为起点向下统计就行了。

    #include
    #define int long long
    using namespace std;
    const int N=1007;
    int n,m,ans[5];
    char mp[N][N];
    
    int check(int x,int y,char ch){
    	int res=0;
    	if(y+4<=m&&mp[x][y+1]==ch&&mp[x][y+2]==ch&&mp[x][y+3]==ch&&mp[x][y+4]==ch) ++res;
    	if(x+4<=n&&mp[x+1][y]==ch&&mp[x+2][y]==ch&&mp[x+3][y]==ch&&mp[x+4][y]==ch) ++res;
    	if(x+4<=n&&y+4<=m&&mp[x+1][y+1]==ch&&mp[x+2][y+2]==ch&&mp[x+3][y+3]==ch&&mp[x+4][y+4]==ch) ++res;
    	if(x-4>=1&&y+4<=m&&mp[x-1][y+1]==ch&&mp[x-2][y+2]==ch&&mp[x-3][y+3]==ch&&mp[x-4][y+4]==ch) ++res;
    	return res;
    }
    
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    	cin>>n>>m;
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=m;++j){
    			cin>>mp[i][j];
    		}
    	}
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=m;++j){
    			ans[mp[i][j]-'0']+=check(i,j,mp[i][j]);
    		}
    	}
    	cout<<ans[1]<<" "<<ans[2]<<"\n";
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    C-消除死域点

    先默认1为根,处理树上的祖先、大小和深度信息,如何第二遍dfs统计答案,假设最开始不删边的答案是 a n s ans ans,在 x x x点与父亲之间删一条边,对答案减少的贡献是 f [ x ] f[x] f[x],那么答案就是 a n s − m a x ( f i ) ans-max(f_i) ansmax(fi),对于 f [ x ] f[x] f[x]可以考虑求向上 s i z e size size不超过 k k k的段,那么整一段切给 x x x显然是最优的,并且下面的答案也不会改变, f [ x ] f[x] f[x]就是这一段的深度差。

    #include
    using namespace std;
    const int N=5e5+7;
    
    int n,k,mx=0,sz[N],dep[N],fa[N][25];
    vector<int>G[N];
    
    void dfs(int x,int f){   //处理树上每个结点的信息 
    	sz[x]=1;dep[x]=dep[f]+1;
    	fa[x][0]=f;
    	for(int i=1;i<=20;++i) fa[x][i]=fa[fa[x][i-1]][i-1]; 
    	for(auto to:G[x]){
    		if(to==f) continue;
    		dfs(to,x);
    		sz[x]+=sz[to];
    	}
    }
    
    void dfs2(int x,int f){
    	if(sz[f]-1>=k){  //如果f的子树大小大于k 
    		int p=x;
    		for(int i=20;i>=0;--i){   //从x出发向上找一段 
    			if(fa[p][i]&&sz[fa[p][i]]-sz[x]<k+1) p=fa[p][i]; 
    		}
    		mx=max(mx,dep[x]-dep[p]); //以x为新根,这一段的结点对答案贡献删除 
    	}
    	for(auto to:G[x]){
    		if(to==f) continue;
    		dfs2(to,x);
    	}
    }
    
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    	cin>>n>>k;
    	for(int i=1,u,v;i<n;++i){
    		cin>>u>>v;
    		G[u].emplace_back(v);
    		G[v].emplace_back(u);
    	}
    	dfs(1,0);
    	int ans=0;
    	for(int i=1;i<=n;++i){
    		if(sz[i]-1>=k) ++ans;
    	}
    	dfs2(1,0);
    	cout<<ans-mx<<"\n";
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    D-七圣召唤

    假设有 n n n次抽卡机会和 m m m种卡

    集齐 m m m张卡的期望抽取次数是 E ( m ) = ∑ i m m i E(m)=\sum_i^m{\frac{m}{i}} E(m)=imim,可以理解为 i m \frac{i}{m} mi的概率抽到第 i i i种,因此期望次数就是它的倒数。

    n n n次的期望种数是 F ( n ) = F ( n − 1 ) + m − F ( n − 1 ) m F(n)=F(n-1)+\frac{m-F(n-1)}{m} F(n)=F(n1)+mmF(n1),可以理解为在 n − 1 n-1 n1抽期望为 x x x种的基础上,抽到新的一种的概率为 m − x m \frac{m-x}{m} mmx

    #include
    using namespace std;
    int n,m;
    
    signed main(){
    	cin>>n>>m;
    	double ans1=0,ans2=0;
    	for(int i=1;i<=m;++i) ans1+=double(m)/i;
    	for(int i=1;i<=n;++i) ans2+=(m-ans2)/m;
    	printf("%.8lf %.8lf\n",ans1,ans2);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    E-病毒危机

    直接暴力找交集就可以了。

    #include
    using namespace std;
    const int N=1e5+7;
    
    int n,m,k,is[N],yes[N];
    
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    	cin>>n>>m;
    	cin>>k;for(int i=1,x;i<=k;++i) cin>>x,is[x]=1;
    	for(int i=2;i<=n;++i){
    		cin>>k;
    		for(int j=1,x;j<=k;++j){
    			cin>>x;
    			if(is[x]) yes[i]=1;
    		}
    	}
    	int ans=1;
    	for(int i=1;i<=n;++i) if(yes[i]) ++ans;
    	cout<<ans<<"\n"; 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    F-互质

    挺离谱的,一开始还以为随机乱搞,但是一个连续的范围怎么可能会有很多数跟一个 1 e 18 1e18 1e18的数互质,所以当然是直接找啦。

    #include
    #define int long long 
    using namespace std;
    
    int n,T;
    
    int read(){	int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x;}
    void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
    
    inline int gcd(int a,int b){
    	return b?gcd(b,a%b):a;
    }
    
    void solve(){
    	n=read();
    	int L=(n+3)/4,R=n/2;
    	for(int i=L;i<=min(R,L+25ll);++i){
    		if(gcd(n,i)==1){
    			write(i);putchar('\n');
    			return;
    		}
    	}
    	puts("-1");		
    
    }
    
    signed main(){
    	T=read();
    	while(T--){
    		solve();
    	} 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    G-栈与公约数

    单点修改、单点查询、区间修改、区间查询,所以是线段树裸题。

    #include
    using namespace std;
    const int N=2e5+1;
    
    inline int gcd(int a,int b){
    	return b?gcd(b,a%b):a;
    }
    
    int n;
    #define ls (p<<1)
    #define rs (p<<1|1)
    #define mid ((l+r)>>1)
    int tr[N<<2],tg[N<<2];
    
    void pushdown(int p,int l,int r){
    	if(tg[p]){
    		tr[ls]=tr[rs]=tg[ls]=tg[rs]=tg[p];
    		tg[p]=0;
    	}
    }
    
    void update(int p,int l,int r,int pos,int v){
    	if(l==r){tr[p]=v;return;}
    	pushdown(p,l,r);
    	if(pos<=mid) update(ls,l,mid,pos,v); 	
    	else update(rs,mid+1,r,pos,v);
    	tr[p]=gcd(tr[ls],tr[rs]);
    }
    
    int query(int p,int l,int r,int pos){
    	if(l==r) return tr[p];
    	pushdown(p,l,r);
    	if(pos<=mid) return query(ls,l,mid,pos);
    	else return query(rs,mid+1,r,pos);
    }
    
    int query_gcd(int p,int l,int r,int ql,int qr){
    	if(ql<=l&&r<=qr) return tr[p];
    	pushdown(p,l,r);
    	if(ql>mid) return query_gcd(rs,mid+1,r,ql,qr);
    	else if(qr<=mid) return query_gcd(ls,l,mid,ql,qr);
    	else return gcd(query_gcd(ls,l,mid,ql,qr),query_gcd(rs,mid+1,r,ql,qr));
    }
    
    void modify(int p,int l,int r,int ql,int qr,int v){
    	if(ql<=l&&r<=qr){
    		tg[p]=tr[p]=v;
    		return;
    	}
    	pushdown(p,l,r);
    	if(ql<=mid) modify(ls,l,mid,ql,qr,v);
    	if(qr>mid) modify(rs,mid+1,r,ql,qr,v);
    	tr[p]=gcd(tr[ls],tr[rs]);
    }
    
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    	cin>>n;
    	int idx=0;
    	for(int i=1,op,x,k;i<=n;++i){
    		cin>>op;
    		if(op==1){
    			cin>>x;
    			++idx;
    			update(1,1,n,idx,x);
    		}else if(op==2){
    			update(1,1,n,idx,0);
    			--idx;
    		}else if(op==3){
    			cout<<query(1,1,n,idx)<<"\n";
    		}else{
    			cin>>k;
    			int md=query_gcd(1,1,n,idx-k+1,idx);
    			modify(1,1,n,idx-k+1,idx,md);
    		}
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    I-图的分割

    读懂了就可以直观感受到他要求一个最小/大生成树,因为克鲁斯卡尔枚举边的过程时,最后一条边保证是把图分成两块并且边权最大值最小的,符合题目要求。

    #include
    using namespace std;
    const int N=2e6+7;
    struct E{
    	int u,v,w,nxt;
    	friend bool operator <(E A,E B){
    		return A.w>B.w;
    	}
    }e[N];
    int cnt=0,head[N];
    inline void addedge(int u,int v,int w){
    	e[++cnt]=(E){u,v,w,head[u]};head[u]=cnt;
    }
    
    int n,m,ans,num=0,fa[N];
    inline int find(int x){
    	return fa[x]==x?x:fa[x]=find(fa[x]); 
    }
    
    signed main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i) fa[i]=i;
    	for(int i=1,u,v,w;i<=n;++i){
    		scanf("%d%d%d",&u,&v,&w);
    		addedge(u,v,w);
    		addedge(v,u,w); 
    	}
    	stable_sort(e+1,e+1+cnt);
    	for(int i=1;i<=cnt;++i){
    		int fx=find(e[i].u),fy=find(e[i].v);
    		if(fx!=fy){
    			++num;
    			fa[fx]=fy;
    			if(num==n-1){ //剩下一个点没有合并 
    				ans=e[i].w;
    				break;
    			}
    		}
    	}
    	cout<<ans<<"\n";
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    K-俄罗斯方块

    不难想,打表可以发现有解的条件是 n % 8 = = 7 ∣ ∣ n % 8 = = 0 n\%8==7||n\%8==0 n%8==7∣∣n%8==0,在纸上画一画就可以得到 7 ∗ 7 7*7 77的三角形、 8 ∗ 8 8*8 88的三角形的构造(下面代码有),那么把他们拼在一块就变成了 8 ∗ 8 8*8 88的正方形了,接下来就模拟一下堆积木的过程。

    #include
    using namespace std;
    
    int n,idx=0,mp[1005][1005]; 
    
    int tri1[10][10]={
    {1},
    {1,1},
    {1,2,2},
    {3,2,2,4},
    {3,3,4,4,4},
    {3,5,6,6,6,7},
    {5,5,5,6,7,7,7}
    };
    
    int tri2[10][10]={
    {1},
    {1,1},
    {1,2,2},
    {3,2,2,4},
    {3,3,4,4,4},
    {3,6,6,6,7,7},
    {5,5,6,8,7,7,9},
    {5,5,8,8,8,9,9,9}
    };
    
    int rct[10][10]={
    {1,10,10,10,11,12,12,12},
    {1,1,10,11,11,11,12,13},
    {1,2,2,14,14,14,13,13},
    {3,2,2,4,14,15,15,13},
    {3,3,4,4,4,15,15,16},
    {3,6,6,6,7,7,16,16},
    {5,5,6,8,7,7,9,16},
    {5,5,8,8,8,9,9,9}
    };
    
    
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    	cin>>n;
    //	for(int i=1;i<=n;++i) frac[i]=frac[i-1]+i;
    //	for(int i=1;i<=n;++i) cout<
    	if(n%8!=7&&n%8!=0){
    		cout<<"NO\n";
    		return 0;
    	}
    	cout<<"YES\n";
    	int sx=1,sy=1,idx=0;
    	if(n%8==7){
    		for(int i=0;i<7;++i){
    			for(int j=0;j<=i;++j){
    				mp[sx+i][sy+j]=tri1[i][j]+idx;
    			}
    		}
    		idx+=7;sx=8;sy=1;
    		for(;sx<n;sx+=8){
    			for(sy=1;sy<=sx;sy+=8){
    				for(int i=0;i<8;++i){
    					for(int j=0;j<8;++j){
    						mp[sx+i][sy+j]=rct[i][j]+idx;
    					}
    				}
    				idx+=16;
    			}
    			for(int i=0;i<7;++i){
    				for(int j=0;j<=i;++j){
    					mp[sx+i+1][sy+j]=tri1[i][j]+idx;
    				}
    			}
    			idx+=7;
    		}
    	}else{
    		for(int i=0;i<8;++i){
    			for(int j=0;j<=i;++j){
    				mp[sx+i][sy+j]=tri2[i][j]+idx;
    			}
    		}
    		idx+=9;sx=9;sy=1;
    		for(;sx<n;sx+=8){
    			for(sy=1;sy<sx;sy+=8){
    				for(int i=0;i<8;++i){
    					for(int j=0;j<8;++j){
    						mp[sx+i][sy+j]=rct[i][j]+idx;
    					}
    				}
    				idx+=16;
    			}
    			for(int i=0;i<8;++i){
    				for(int j=0;j<=i;++j){
    					mp[sx+i][sy+j]=tri2[i][j]+idx;
    				}
    			}
    			idx+=9;
    		}		
    	}
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=i;++j){
    			//printf("%2d ",mp[i][j]);
    			cout<<mp[i][j]<<" ";
    		}
    		cout<<"\n";
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    M-画画

    贪心把同时不符合行和列的奇偶性的格子改掉,必然是最优的。

    #include
    using namespace std;
    const int N=1007;
    
    int n,numx[N],numy[N];
    char mp[N][N];
    
    void solve(){
    	cin>>n;
    	for(int i=0;i<n;++i){
    		for(int j=0;j<n;++j){
    			mp[i][j]='1';
    			++numx[i];++numy[j];
    		}
    	}
    	for(int i=n-1;i>=0;--i){
    		for(int j=n-1;j>=0;--j){
    			if((numx[i]&1)!=(i&1)&&(numy[j]&1)!=(j&1)){
    				--numx[i];--numy[j];
    				mp[i][j]='0';
    			}
    		}
    	}
    	for(int i=0;i<n;++i){
    		for(int j=0;j<n;++j){
    			cout<<mp[i][j];
    		}
    		cout<<"\n";
    	}
    	for(int i=0;i<n;++i) numx[i]=numy[i]=0;
    }
    
    signed main(){
    	int t;
    	cin>>t;
    	while(t--){
    		solve();
    	}
    	return 0; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 相关阅读:
    C语言K&R圣经笔记 2.4声明 2.5算术操作符 2.6关系和逻辑操作符
    新能源中期财报:有人上天入地有人还在苦苦挣扎
    搭建第一个区块链网络
    Unity Shader - if 和 keyword 的指令比较
    eMMC编程基础 - (一)eMMC相关术语和概念
    WPF必须掌握的技能之自定义控件——实战:自制上传文件显示进度按钮
    科创人·望繁信创始人索强:中国版流程挖掘注定有完全不同的活法
    Canvas初级小案例
    Matlab:正则表达式
    ubuntu 20.04 Kimera semantic 运行记录
  • 原文地址:https://blog.csdn.net/SC_Linno/article/details/127522877