杭电多校补题集

杭电多校补提集

  1. Calculus
    题目描述
    在这里插入图片描述
    因为对于单个函数来说,都要保证求和是收敛的。那么显然常熟求和不收敛,C/x可以提出C,就是经典求和,记结论是发散的,其他同样提出C发现是发散的,所以系数C必须是0.对于最后一个函数,因为规定C是0到1e9的整数,所以C只能是0才能求和收敛。那么只需要判断所有系数是否为0就做完了。
#include<bits/stdc++.h>
using namespace std;
int t,n;char s[110];
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%s",s+1);
		n=strlen(s+1);
		bool flag=1;
		for(int i=1;i<=n;i++){
			if(s[i]>'0'&&s[i]<='9'){
				flag=0;break;
			}
		}
		if(flag) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}
  1. License Plate Recognition

    题目描述在这里插入图片描述
    大致思路就是,出了“川”和“鄂”,其他的所有汉字和字母都是连续的,也就是说我们只需要找到两个完全没有#的列,他们中间夹的就是一个字母或者汉字。上面两种不连续的汉字是特例,可以通过特判解决。
#include<bits/stdc++.h>
using namespace std;
char s[31][110];
int t;int a[110],cnt;
int main(){
	scanf("%d",&t);
	for(int k=1;k<=t;k++){
		printf("Case #%d:\n",k);cnt=0;
		for(int i=1;i<=30;i++) scanf("%s",s[i]+1);
		int l,r,tmp=0,lef;
		for(int j=1;j<=100;j++){
			bool flag=1;
			for(int i=1;i<=30;i++){
				if(s[i][j]=='#') flag=0;
			}
			if(flag){
				a[++cnt]=j;
			}
		}
		//for(int i=1;i<=cnt;i++) printf("%d ",a[i]);
		//cout<<endl;
		for(int i=1;i<=cnt;i++){
			if(a[i+1]-a[i]==4&&a[i+5]-a[i+4]==2&&a[i+10]-a[i+9]==2){
				printf("%d %d\n",a[i]+1,a[i+10]-1);i=i+10;
			}else if(a[i+1]-a[i]==10&&a[i+2]-a[i+1]==6){
				printf("%d %d\n",a[i]+1,a[i+2]-1);i=i+2;
			}else if(a[i+1]-a[i]>1){
				printf("%d %d\n",a[i]+1,a[i+1]-1);
			}
		}
		for(int i=1;i<=cnt;i++) a[i]=0;
	}
	return 0;
}

3.Kanade Loves Maze Designing
在这里插入图片描述
n很小,点权也不大。直接n方跑n遍,dfs+回溯即可。需要注意的是奇奇怪怪的输入形式以及输出格式。

#include<bits/stdc++.h>
using namespace std;
const int p1=1e9+7;
const int p2=1e9+9;
const int N=2e3+10;
int t,n,tot,lin[N],ver[N*2],Next[N*2],num[N],ans[N],v[N];
void add(int x,int y){
	ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;
}
void dfs(int x,int fa,int now){
	num[v[x]]++;
	for(int i=lin[x];i;i=Next[i]){
		int y=ver[i];
		if(y==fa) continue;
		if(!num[v[y]]) ans[y]=now+1;
		else ans[y]=now;
		dfs(y,x,ans[y]);
	}
	num[v[x]]--;
}
int main(){
	scanf("%d",&t);
	while(t--){
		tot=0;
		memset(lin,0,sizeof(lin));
		scanf("%d",&n);
		for(int i=1;i<n;i++){
			int x,y;scanf("%d",&x);
			add(x,i+1);add(i+1,x);
		}
		for(int i=1;i<=n;i++){
			scanf("%d",&v[i]);
		}
		for(int i=1;i<=n;i++){
			dfs(i,0,1);
			ans[i]=1;
			int tmp1=1,tmp2=1;int sum1=0,sum2=0;
			for(int j=1;j<=n;j++){
				sum1=(sum1+1LL*tmp1*ans[j]%p1)%p1;
				sum2=(sum2+1LL*tmp2*ans[j]%p2)%p2;
				tmp1=1LL*tmp1*19560929%p1;
				tmp2=1LL*tmp2*19560929%p2;
				//cout<<ans[j]<<' ';
			}
			//cout<<endl;
			printf("%d %d\n",sum1,sum2);
		}
	}
	return 0;
}

4.Lawn of the Dead
在这里插入图片描述
关键点在于只能向右或者向下走。假如一个障碍点,那么它右边的点想要到达,必须是从上面走过来的。那么对于两个障碍点中间这一段区间的点,能到达哪些点,取决于上一层的对应区间,能到达哪些点。问题转化成了区间整体赋值问题,我们可以把能到达的点赋值1,不能到达的赋值2.线段树即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
const int inf=1e9;
vector<int>f[N];
int sum[N*4],d[N*4];
int t,n,m,k;
ll ans;
void pushdown(int p,int l,int r){
	if(d[p]==-1) return;
	int mid=l+r>>1;
	sum[p*2]=(mid-l+1)*d[p];
	sum[p*2+1]=(r-mid)*d[p];
	d[p*2]=d[p*2+1]=d[p];
	d[p]=-1;
}
void update(int p,int l,int r,int L,int R,int v){
	if(L>R) return;
	if(l>=L&&r<=R){
		sum[p]=(r-l+1)*v;
		d[p]=v;
		return;
	}//cout<<l<<' '<<r<<' '<<sum[p]<<' '<<L<<' '<<R<<' '<<v<<endl;
	pushdown(p,l,r);
	int mid=(l+r)/2;
	if(L<=mid) update(p*2,l,mid,L,R,v);
	if(R>mid) update(p*2+1,mid+1,r,L,R,v);
	sum[p]=sum[p*2]+sum[p*2+1];
}
int query(int p,int l,int r,int L,int R){
	//cout<<l<<' '<<r<<' '<<sum[p]<<' '<<L<<' '<<R<<endl;
	if(l>=L&&r<=R){
		if(sum[p]==r-l+1){
			return l;
		}
		if(sum[p]==0) return inf;
	}
	if(r<L||l>R) return inf;
	pushdown(p,l,r);
	int mid=l+r>>1;
	int temp=query(p*2,l,mid,L,R);
	//cout<<l<<' '<<r<<' '<<sum[p]<<' '<<temp<<endl;
	if(temp!=inf) return temp;
	else return query(p*2+1,mid+1,r,L,R);
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d",&n,&m,&k);
		for(int i=1;i<=n;i++) f[i].push_back(0);
		for(int i=1;i<=k;i++){
			int x,y;scanf("%d%d",&x,&y);
			f[x].push_back(y);
		}
		for(int i=1;i<=n;i++){
			f[i].push_back(m+1);
			sort(f[i].begin(),f[i].end());
		}
		ans=0;
		ans+=f[1][1]-1;
		update(1,1,m,1,f[1][1]-1,1);
		for(int i=2;i<=n;i++){
			for(int j=0;j<f[i].size()-1;j++){
				int l=f[i][j]+1,r=f[i][j+1]-1;
				//cout<<r+1<<endl;
				if(r+1!=m+1)
				update(1,1,m,r+1,r+1,0);
				//cout<<sum[1]<<endl;cout<<endl;
				if(l>r) {
					continue;
				}
			//	cout<<l<<' '<<r<<endl;
				int temp=query(1,1,m,l,r);
				//cout<<endl;
				//cout<<sum(1)<<endl;
				//cout<<l<<' '<<r<<' '<<temp<<endl;
				if(temp!=inf){
					ans+=r-temp+1;
					update(1,1,m,l,min(r+1,m),0);
					if(i!=n)
					update(1,1,m,temp,r,1);
				}
			}
		}
		for(int i=1;i<=n;i++) f[i].clear();
		cout<<ans<<endl;
	}
	return 0;
}

5.杭电9 1007 链表
题目描述:从1开始递增的往一个deque里插数,L就往左插,R就往又插,G x代表删去deque里的x,Q代表查询中位数。
分析:重点在于,每次插入一个新数或者删掉一个数,中位数只有三种情况:不动、向左移一个位置、向右移一个位置。而且到底什么情况很好判断,因此直接上链表。(太久没写链表,bug有点多,调了好久)

#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10;
int q,cnt,tot,l,r,mid,now;
int po[N];
struct node{
	int lef,rig,pos;
	int v;
}e[N];
int main(){
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout); 
	scanf("%d",&q);
	int n=1;
	while(q--){
		char ch=getchar();
		while(ch=='\n') ch=getchar();
		if(ch=='L'||ch=='R'){
			if(tot==0){
				cnt++;tot++;
				e[cnt].pos=0;e[cnt].v=n;n++;
				l=r=cnt;mid=l;po[n-1]=cnt;
			}else{
				if(ch=='L'){
					e[l].lef=++cnt;tot++;
					e[cnt].rig=l;e[cnt].pos=-1;
					l=cnt;e[l].v=n;po[n]=cnt;n++;
					if(tot%2){
						int temp=mid;
						mid=e[mid].lef;e[temp].pos=1;
						e[mid].pos=0;
					}
					//cout<<e[e[2].lef].v<<' '<<e[e[2].rig].v<<endl;
				}else{
					e[r].rig=++cnt;tot++;
					e[cnt].lef=r;r=cnt;e[cnt].pos=1;
					e[cnt].v=n;po[n]=cnt;n++;
					if(tot%2==0){
						e[mid].pos=-1;
						e[e[mid].rig].pos=0;
						mid=e[mid].rig;
					}
				}
			}
		}else if(ch=='Q'){
			printf("%d\n",e[mid].v);
		}else if(ch=='G'){
			int x;scanf("%d",&x);
			now=po[x];
			tot--;
			if((tot+1)%2==0){
				if(e[now].pos!=-1){
					int temp=mid;
					mid=e[mid].lef;
					e[mid].pos=0;e[temp].pos=1;
				}
			}else{
				if(e[now].pos!=1){
					int temp=mid;
					mid=e[mid].rig;
					e[mid].pos=0;e[temp].pos=-1;
				}
			}
			if(now==l){
				int R=e[now].rig;e[R].lef=0;
				l=R;
			}else if(now==r){
				int L=e[now].lef;e[L].rig=0;
				r=L;
			}else{
				int L=e[now].lef,R=e[now].rig;
				e[L].rig=R;e[R].lef=L;
			}
			
			//cout<<e[e[2].lef].v<<' '<<e[e[2].rig].v<<endl;
		}
	}
	return 0;
}

6.杭电9 1002 Dota2
题目描述
在这里插入图片描述
假设现在我知道两个人最终跑了k轮,那么可以想到,如果k是偶数,对于后手来说,他最后一次移动一定是移动到当前列的最小值。但是先手想要值越大越好,因此最终到达的一定是列的最小值里最大的那一个。如果k是奇数且k大于1,那么再先手最后一次操作前,后手会干扰先手,那么取到的一定是行的最大值里的最小值。如果k=1,那么先手会直接取到第一行的最大值。
但是问题在于,这道题并不一定跑k轮。每个人是可以随时结束游戏的。但其实对我们答案有影响的只是进行轮数的奇偶性。如果k是奇数,那么先手一定不会结束游戏,因为如果先手结束游戏,相当于跑了偶数轮,这对他是不利的。后手自然也不会结束游戏,因为这个操作做了,轮数仍然是奇数,对它毫无意义。同理,如果k是偶数,先手结束游戏毫无意义,后手结束游戏对他不利。
总的来说就是先手后手虽然可以随时结束游戏,但其实他们最终还是跑满了k轮。
唯一需要注意的是,先手可以第一时间结束游戏,这样他就可以取到a11这个值。
7.C. Pty loves lines
题目描述

在这里插入图片描述
分析
1、首先应想到怎么dp。考虑k条边可能产生的交点个数时,可以由i条边产生的交点数递推过来。设dp[i][j]表示i条边,产生j个交点是否成立,如果成立,那么dp[k][(k-i)*i+j]一定成立。因此可以写一个 n 4 n^{4} n4的dp。这是hdu一道原题,n是20,可以通过.
2、但是现在这道题n是700,考虑如何优化。容易发现,用dp[i]推dp[k]时,只是把dp[i]的状态数组右移了(k-i)*i个位置。可以用bitset优化递推,复杂度变为 n 4 / 32 n^4/32 n4/32.
3、虽然bitset快了很多,但本地实测跑了7s左右,仍然不能通过本题。其实通过样例就可以发现,对于每个k来说,后面有很长一段答案是连续的。打表发现最大不能交出来的点不超过35000,复杂度变为 n 2 ∗ 35000 / 32 n^2*35000/32 n235000/32.可以通过本题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值