杭电第七场 题解

本文介绍了四个编程题目,涉及数学分析、模拟、图论和字符串处理等算法。签到题关注冰红茶消耗问题,基本题包括贪吃蛇游戏的最短路径模拟和极限函数的性质,进阶题探讨了完美二叉树的面积计算和字符串子区间的权值和。这些题目展示了算法在解决实际问题中的应用。
摘要由CSDN通过智能技术生成

比赛传送门
作者: fn


签到题

1010题 Smzzl with Tropical Taste / Smzzl与热带风味冰红茶

题目大意
游泳池里有 V V V 升的冰红茶,开始时 V = 1 V=1 V=1升, 主人倒冰红茶的速度是 q V qV qV 升/秒(速度是 V V V 的正比例函数),男孩喝冰红茶的速度是 p V pV pV 升/秒,喝完为止。

给定 p , q p,q p,q ,求解:对于任意 G > 0 G>0 G>0 ,是否存在一个 T > 0 T>0 T>0 ,对于任意 t > T t>T t>T ,男孩在 t t t 秒后喝了超过 G G G 升的冰红茶。

考察内容
数学

分析
p > q p>q p>q 时,喝冰红茶的数量有限;当 p ≤ q p≤q pq 时,喝冰红茶的数量无限。

#include<bits/stdc++.h> // 题1010 
#define ll long long
using namespace std;
ll n,m;
int main(){ 
	ios::sync_with_stdio(0); cin.tie(0);
	int t; cin>>t;
	double p,q;
	while(t--){
		cin>>p>>q;
		if(p<=q){ // 喝无穷大 
			cout<<"N0 M0R3 BL4CK 1CE TEA!"<<endl;
		}
		else{ // 喝有限 
			cout<<"ENJ0Y YOURS3LF!"<<endl;
		}
	}
	return 0;
}

基本题

1008题 Smzzl with Greedy Snake / Smzzl与贪吃蛇

题目大意
Smzzl要为贪吃蛇做一个AI。

在这个游戏中,蛇需要1单位的时间向前移动1单位的长度。 蛇旋转90度也需要1单位的时间。 最初地图上有一种食物。 蛇吃完每一种食物后,下一种食物就出现了。

Smzzl当然希望蛇尽可能快地吃完食物,所以他需要尽量减少蛇吃每一种食物的时间。 请输出一个有效的操作序列。

考察内容
模拟

分析
这道题中只考虑蛇头,蛇没有长度。所以在吃每个食物时尽可能直走,根据食物所在的方向和当前蛇头的方向分类讨论,模拟走最短路径的过程即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+10;
ll n,m,a[N];

int X[4]={0,1,0,-1}, Y[4]={1,0,-1,0};

int main(){ // 已AC
	ios::sync_with_stdio(0); cin.tie(0);
	int t; cin>>t;
	ll x,y,d; 
	while(t--){
		cin>>x>>y>>d;
		cin>>n;
		ll x2,y2;
		for(int i=1;i<=n;i++){
			cin>>x2>>y2;
			ll dx=x2-x,dy=y2-y;
			if(dx>0 && dy>0){ // 右下角 
				if(d==0){ // 头朝右 
					for(int i=1;i<=dy;i++) // 右 
						cout<<'f';
					
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dx;i++) // 下 
						cout<<'f';
				}
				else if(d==1){ // 下 
					for(int i=1;i<=dx;i++)
						cout<<'f';
					
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dy;i++)
						cout<<'f';
				}
				else if(d==2){ // 左 
					cout<<'u'; d=(d+3)%4; // 变成头朝下 
					for(int i=1;i<=dx;i++)
						cout<<'f';
					
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dy;i++)
						cout<<'f';
				}
				else{ // 上 
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dy;i++)
						cout<<'f';
					
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dx;i++)
						cout<<'f';
				}
			}
			else if(dx<0 && dy<0){  // 左上角 
				dx=abs(dx),dy=abs(dy);
				if(d==0){ // 头朝右 
					cout<<'u'; d=(d+3)%4;
					
					for(int i=1;i<=dx;i++) cout<<'f';
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dy;i++) cout<<'f';
				}
				else if(d==1){ // 下 
					cout<<'c'; d=(d+1)%4;
					
					for(int i=1;i<=dy;i++) cout<<'f';
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dx;i++) cout<<'f';
				}
				else if(d==2){ // 左 
					for(int i=1;i<=dy;i++) cout<<'f';
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dx;i++) cout<<'f';
				}
				else{ // 上 
					for(int i=1;i<=dx;i++) cout<<'f';
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dy;i++) cout<<'f';
				}
			} 
			else if(dx>0 && dy<0){ // 左下角 
				dx=abs(dx),dy=abs(dy);
				
				if(d==0){ // 头朝右 
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dx;i++) cout<<'f';
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dy;i++) cout<<'f';
				}
				else if(d==1){ // 下 
					for(int i=1;i<=dx;i++) cout<<'f';
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dy;i++) cout<<'f';
				}
				else if(d==2){ // 左 
					for(int i=1;i<=dy;i++) cout<<'f';
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dx;i++) cout<<'f';
				}
				else{ // 上 
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dy;i++) cout<<'f';
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dx;i++) cout<<'f';
				}
			}
			else if(dx<0 && dy>0){ // 右上角 
				dx=abs(dx),dy=abs(dy);
				
				if(d==0){ // 头朝右 
					for(int i=1;i<=dy;i++) cout<<'f';
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dx;i++) cout<<'f';
				}
				else if(d==1){ // 下 
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dy;i++) cout<<'f';
					cout<<'u'; d=(d+3)%4;
					for(int i=1;i<=dx;i++) cout<<'f';
				}
				else if(d==2){ // 左 
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dx;i++) cout<<'f';
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dy;i++) cout<<'f';
				}
				else{ // 上 
					for(int i=1;i<=dx;i++) cout<<'f';
					cout<<'c'; d=(d+1)%4;
					for(int i=1;i<=dy;i++) cout<<'f';
				}
				
			}
			else if(dx==0){ // 直线 
				if(dy>0){
					if(d==0){ // 头朝右 
						
					}
					else if(d==1){ // 下 
						cout<<'u'; d=(d+3)%4;
					}
					else if(d==2){ // 左 
						cout<<'c'; cout<<'c'; d=(d+1)%4; d=(d+1)%4;
					}
					else{ // 上 
						cout<<'c'; d=(d+1)%4;
					}
					for(int i=1;i<=dy;i++){
						cout<<'f';
					}
				}
				else{ // dy<0
					dy=-dy;
					if(d==0){ // 头朝右 
						cout<<'c'; cout<<'c'; d=(d+1)%4; d=(d+1)%4;
					}
					else if(d==1){ // 下 
						cout<<'c'; d=(d+1)%4;
					}
					else if(d==2){ // 左 
	
					}
					else{ // 上 
						cout<<'u'; d=(d+3)%4;
					}
					for(int i=1;i<=dy;i++){
						cout<<'f';
					}
				}
			}
			else if(dy==0){
				if(dx>0){
					if(d==0){
						cout<<'c'; d=(d+1)%4;
					}
					else if(d==1){
						
					}
					else if(d==2){
						cout<<'u'; d=(d+3)%4;
					}
					else{ // 3
						cout<<'u'; d=(d+3)%4; cout<<'u'; d=(d+3)%4;
					}
					for(int i=1;i<=dx;i++) cout<<'f';
				}
				else{ // dx<0
					dx=-dx;
					if(d==0){
						cout<<'u'; d=(d+3)%4;
					}
					else if(d==1){
						cout<<'c'; d=(d+1)%4; cout<<'c'; d=(d+1)%4;
					}
					else if(d==2){
						cout<<'c'; d=(d+1)%4;
					}
					else{ // 3
						
					}
					for(int i=1;i<=dx;i++) cout<<'f';
				}
			}
			x=x2;
			y=y2;
		}
		cout<<endl;
	}
	return 0;
}

1007题 Link with Limit / Link与极限

题目大意
Link有一个函数 f ( x ) f(x) f(x) ,其中 x x x f ( x ) f(x) f(x) 都是 [ 1 , n ] [1,n] [1,n] 中的整数。

f n ( x ) = f ( f n − 1 ( x ) ) f_n(x)=f(f_{n−1}(x)) fn(x)=f(fn1(x)) f 1 ( x ) = f ( x ) f_1(x)=f(x) f1(x)=f(x) ,关于 x x x 的函数 g ( x ) g(x) g(x) 为:
g ( x ) = lim ⁡ n → + ∞ 1 n ∑ i = 1 n f i ( x ) g (x) =\lim\limits_{n→+∞}\frac{1}{n}∑\limits_{i = 1}^{n}f_i (x) g(x)=n+limn1i=1nfi(x)

他想知道对于所有 x ∈ [ 1 , n ] x∈[1,n] x[1,n] , g ( x ) g(x) g(x) 是否相同。

考察内容
数学,图论,dfs

分析
第一步:数学
分析 g ( x ) g (x) g(x) ,其中 f i ( x ) f_i(x) fi(x) 的意思就是对 x x x 套用 i i i f ( x ) f(x) f(x)
建一张有向图,点 x x x 向点 f ( x ) f(x) f(x) 连边,那么 f f f 迭代的过程实质上是在图上行走的过程。 x x x f ( x ) f(x) f(x) 都是 [ 1 , n ] [1,n] [1,n] 中的整数,所以必然会形成环。 n → + ∞ n→+∞ n+ 时,只有环上的点权才会对极限有影响。原题转化为问每一个环的点权平均值是否相同。

第二步:图论
找出所有环并求出点权平均值。

先连上反向边。利用 dfs 遍历结点找环,找到时计算点权平均值,然后反向遍历所有可以到达这个环的结点,这些结点的 g ( x ) g (x) g(x) 都是相同的,不会对答案造成影响,之后不再遍历。
复杂度 O ( n ) O(n) O(n)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
ll n,m,f[N],an; // f[i]指向i下一个结点 
vector<int> f2[N]; // 反向边

int loop[N]; // loop[i]==1表示i可以到达已知的环 

double sum=0,ans=0;
double g[N]; //
ll ff[N]; 
int vis[N]; 

void dfs2(int x){ // 遍历反向边 
	if(loop[x]==1)return;
	
	loop[x]=1;
	for(auto e:f2[x]){
		dfs2(e);
	} 
} 

void dfs(int x,ll cnt){ // cnt记录环大小 
    if(an==0)return; // 剪枝 
    
    ff[x]=sum;
    sum+=f[x];
    vis[x]=cnt;
    g[x]=sum;
    if(vis[f[x]]==0){
        dfs(f[x],cnt+1);
        return;
    }
    else{
		dfs2(x); // 标记可以到达环 
    	
        int s=cnt-vis[f[x]]+1;
        if(s==0) return;
        sum=(g[x]-ff[f[x]])*1.0/s;
        //cout<<sum<<" "<<s<<endl;
        
        if(ans==0) ans=sum;
        if(ans!=sum) an=0;
    }
    return;
}

void init(int n){
	memset(g,0,sizeof(g[0])*(n+1));
    memset(ff,0,sizeof(ff[0])*(n+1));
    memset(f,0,sizeof(f[0])*(n+1));
    memset(loop,0,sizeof(loop[0])*(n+1));
    memset(ff,0,sizeof(ff[0])*(n+1));
    memset(vis,0,sizeof(vis[0])*(n+1));
    for(int i=1;i<=n;i++){
        f2[i].clear();
    }
}

int main(){ // AC
    ios::sync_with_stdio(0); cin.tie(0);
    int t; cin>>t;
    while(t--){
    	cin>>n;
		init(n);
        
        an=1;
        ans=0;
        sum=0;
        for(int i=1;i<=n;i++){
            cin>>f[i]; // 正向边 
            f2[f[i]].push_back(i); // 加一条反向边 
        }
        for(int i=1;i<=n;i++){
            if(loop[i]==0){ // 无法到达已知环 
                sum=0;
                dfs(i,1);
            }
        }
        if(an==0) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
    }
    return 0;
}
/*
10
6
5 3 3 3 1 4
YES 

10
2 3 4 5 6 7 8 9 10 1
YES
*/

1003题 Fall with Trees / Fall与树

题目大意
Fall想要画一个完美的二叉树。

我们首先规定树中所有具有相同深度的节点在平面上也具有相同的y坐标。 将相同深度的节点定义为相同层次的节点,那么完美二叉树有四个属性。

  • 满二叉树。
  • 相邻两层节点的y坐标差为常数。
  • 同一层次上两个相邻节点的x坐标差为常数。
  • 每个节点的x坐标是其子节点x坐标的平均值。

Fall已经画出了这个二叉树的根节点和它的左右两个子节点。 现在Fall打算画出总共k个层次,他想知道这个完美二叉树的所有节点的凸包的面积是多少。

完美二叉树的例子:
完美二叉树

考察内容
数学,等比数列求和

分析
等比数列求每一层的面积和

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int k,t,x0,y0,x1,y1,x2,y2;

int main(){ //
    cin>>t;
    while(t--){
        scanf("%d",&k);
        scanf("%d%d%d%d%d%d",&x0,&y0,&x1,&y1,&x2,&y2);
        
        ll d=x2-x1,h=y0-y1,S=(2ll*k-5)*d*h,p=60000ll*d*h;
		for (int i=1;i<=k&&p;i++) p>>=1;
		S+=p/10000,p%=10000,k=p%10,p/=10;
		
		printf("%lld.%03lld\n",S,p+(k>=5));	
    }
}

进阶题

1012题 Yiwen with Sqc / Yiwen和字符串

题目大意
定义权值为区间里面每个字母出现次数的平方和,给定一个字符串,计算字符串所有子区间的权值和。

考察内容
差分,字符串,复杂度优化

分析
不同字母之间没有影响,分别考虑每一种字母。

#include<bits/stdc++.h>
using namespace std;

const int P = 998244353;
int n;
char ch[200005];

int solve(vector<int> vec){
	vec.push_back(n+1);
	int ans=0,per=0,dif=0;
	for (int i=1;i<vec.size();i++){
		ans=(1ll*ans+1ll*per*(vec[i]-vec[i-1]))%P;
		dif=(1ll*dif+2ll*vec[i-1]+vec[i]-vec[i-1])%P;
		per=(per+dif)%P;	
	}
	return ans;
}

void solve(){
	scanf("%s",ch+1);
	n=strlen(ch+1);
	ch[n+1]='\0';
	vector<int> a[28];
	for (int i=0;i<=25;i++) a[i].push_back(0);
	for (int i=1;i<=n;i++) {
		a[ch[i]-'a'].push_back(i);
	}
	int ans=0;
	for (int i=0;i<=25;i++) {
		ans=(ans+solve(a[i]))%P;
	}
	printf("%d\n",ans);
}
int main(){
	int t;
	scanf("%d",&t);
	while (t--) solve();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值