2019ccpc女生赛+补题

        这周末要打女生赛了,想着和队友练一场,在cf只找到2021年的,由于去年那场打过就不再打了,杭电oj上面最近的也是2019年的,就打了这场。不过题面真的是。。。一言难尽,tree那题题面说三棵树其实是一棵树,Tetris俄罗斯方块那题是道签到,题面input一行两个整数实际上是多行输入,一直以为思路错了卡了很久。。。。。。改成多行输入就对了。

ps:在这个页面交不了题,只能看题,在题库中对应题号是6544-6554.。

 1001-Ticket

思路:

签到,只需记录当前共消费多少,每次买票时根据总消费加上打折的票价。

代码:

#include<bits/stdc++.h>
const int maxn=1010;
using namespace std;
#define ll long long
double n,a[maxn];
int main()
{
	double sum=0;
	double res=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		if(sum<100)sum+=a[i];
		else if(sum>=100&&sum<150)sum+=a[i]*0.8;
		else if(sum>=150&&sum<400)sum+=a[i]*0.5;
		else sum+=a[i];
	}
	printf("%.2lf\n",sum);
	
	return 0;
}

1002-Gcd

思路:

将1-n的数分为两组,使得这两组数的gcd最大。

也是签到。

直接求1-n的和sum的第二大公因数,第一大公因数是sum本身,要分成两组数所以必不可能为sum,因此找第二大公因数。若a%x==0,b%x==0,则一定有(a+b)%x==0.只要其中一堆数和为第二大公因数即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
int main(){
	cin>>n;
	ll sum=(1+n)*n/2;
	int temp=1;
	for(int i=2;i<=sqrt(sum);i++)
	{
		if(sum%i==0)
		{
			cout<<sum/i<<endl;
			temp=-1;
			break;
		}
	}
	if(temp==1)cout<<"1"<<endl;
	return 0;
}

1003-Functon

思路:

这题是学姐写的,没读题。

代码:

#include <bits/stdc++.h>
using namespace std;
#define N 1000005
typedef long long ll;
struct node{int idx;ll x;ll val;};
bool operator < (node a,node b){return a.val>b.val;}
bool operator > (node a,node b){return a.val<b.val;}
ll n,m,a[N],b[N],c[N];
int main()
{
	cin>>n>>m;
	priority_queue<node> q;
	ll ans=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
		ans+=a[i]+b[i]+c[i];
		q.push({i,1,3*a[i]+b[i]});
	}
	m-=n;
	while(m--)
	{
		node t=q.top();q.pop();
		ans+=t.val;
		t.x=t.x+1;
		t.val=2*a[t.idx]*t.x+a[t.idx]+b[t.idx];
		q.push(t);
	}
	cout<<ans<<endl;
	return 0;
}

1004-Tree

思路:

训练时没写出来,感觉是数据结构题,应该可以用线段树写,但是学的不精不会写。

赛后看了一下题解说是树链剖分板子题,马上去学了树链剖分,补了。

树链剖分+线段树维护。

n个节点n-1条边,保证无环,因此相当于是一棵树,任意两个节点之间的路径实际上只有一条,所以不需要计算最短路径。

原本每次修改区间上的节点可能会超时,但由于同个节点修改不超过10次之后节点权值最多只能是1,因此加一个maxx判断当前节点区间最大值,如果在修改时发现最大值为1那么就不需要再修改了,这样复杂度就不会超时。

代码:

#include<bits/stdc++.h>
#define ll long long
const int N=1e5+10,M=N*2;
using namespace std;
inline ll max(ll a,ll b){return a > b ? a : b;}
int n,m,cnt,idx;
int id[N],nw[N],dep[N],sz[N],top[N],fa[N],son[N],w[N],h[N],e[M],ne[M];
//w[N]存节点权值,e[N],ne[N]邻接表存人数
//id[N]存节点dfs编号,nw[N]存每个编号(dfs)的权值,dep[N]存每个节点深度,sz[N]存以节点为根的子树大小
//top[N]存每个重链顶点,fa[N]存每个节点父节点,son[N]存每个节点重儿子,e[N]存边节点
struct tree
{
  int l,r;
  ll maxx,sum;
}tr[N*4];//线段树
void add(int a,int b)
{
  //存边,更新邻接表
  e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
//dfs所有节点的重儿子,更新树节点信息
void dfs1(int u,int father,int depth)
{
  dep[u]=depth,fa[u]=father,sz[u]=1;
  for(int i=h[u];~i;i=ne[i])//遍历u邻接表
  {
    int j=e[i];
    if(j==father) continue;//当前边节点为父节点
    dfs1(j,u,depth+1);
    sz[u]+=sz[j];//更新以u为根节点的子树大小;
    if(sz[son[u]]<sz[j])son[u]=j;//更新u节点的重儿子,即u的子节点中以该节点为跟的根的子树更大的那个节点
  }
}
//dfs重链重新编号,t是重链的顶点每条重链都是连续的编号
//先更新每条重链的重儿子,再更新轻儿子
void dfs2(int u,int t)
{
  id[u]=++cnt,nw[cnt]=w[u],top[u]=t;
  if(!son[u])return;//当前为叶节点
  dfs2(son[u],t);//重儿子重链剖分
  //处理轻儿子
  for(int i=h[u];~i;i=ne[i])
  {
    int j=e[i];
    if(j==fa[u]||j==son[u])continue;//当前节点为父节点或重儿子
    dfs2(j,j);//轻儿子的重链顶点就是他自己
  }
}
void pushup(int u)
{
  tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
  tr[u].maxx=max(tr[u<<1].maxx,tr[u<<1|1].maxx);
  //for(int i=1;i<=7;i++)cout<<i<<":   ["<<tr[i].l<<tr[i].r<<"]   max:"<<tr[i].maxx<<"  sum:"<<tr[i].sum<<endl;
}
//线段树建树
void build(int u,int l,int r)
{
  tr[u]={l,r,nw[r],nw[r]};
  if(l==r)//当前为根节点
    return;
  int mid=l+r>>1;
  build(u<<1,l,mid);build(u<<1|1,mid+1,r);
  pushup(u);
}
//线段树进行修改并更新
void update(int u,int l,int r)
{
  if(tr[u].maxx<=1)return;
  if(tr[u].l==tr[u].r)//当前为叶子节点,更新
  {
    tr[u].maxx=sqrt(tr[u].maxx);
    tr[u].sum=sqrt(tr[u].sum);
    return;
  }
  int mid=tr[u].l+tr[u].r>>1;
  if(l<=mid&&tr[u<<1].maxx>1)update(u<<1,l,r);
  if(r>mid&&tr[u<<1|1].maxx>1)update(u<<1|1,l,r);
  pushup(u);
}
//线段树进行查询区间和
ll query(int u,int l,int r)
{
  if(l<=tr[u].l&&r>=tr[u].r)return tr[u].sum;
  int mid=tr[u].l+tr[u].r>>1;
  ll res=0;
  if(l<=mid) res+=query(u<<1,l,r);
  if(r>mid) res+=query(u<<1|1,l,r);
  return res;
}
//更新节点之间路径
void update_path(int u,int v)
{
  while(top[u]!=top[v])//当两个节点的重链的头节点不一样,即不在一条重链上,优先走更低的重链
  {
    if(dep[top[u]]<dep[top[v]])swap(u,v);
    update(1,id[top[u]],id[u]); 
    u=fa[top[u]];
  }
  if(dep[u]<dep[v])swap(u,v);
  update(1,id[v],id[u]);//在同一重链中,处理剩余区间
}
//查询路径u-v上所有节点权值和
ll query_path(int u,int v)
{
  ll res=0;
  while(top[u]!=top[v])//向上爬找到相同重链
  {
    if(dep[top[u]]<dep[top[v]])swap(u,v);
    res+=query(1,id[top[u]],id[u]);
    u=fa[top[u]];
  }
  if(dep[u]<dep[v])swap(u,v);
  res+=query(1,id[v],id[u]);//在同一重链中,处理剩余区间
  return res;
}
int main()
{
  scanf("%d%d",&n,&m);
  memset(h,-1,sizeof(h));//赋值为负无穷大
  for(int i=1;i<=n;i++)scanf("%d",&w[i]);//节点权值
  int a,b;
  for(int i=1;i<n;i++)
  {
    scanf("%d%d",&a,&b);
    add(a,b);add(b,a);//加边a-b
  }
  dfs1(1,-1,1);//dfs1预处理
  dfs2(1,1);//dfs2树链剖分
  build(1,1,n);
  for(int i=1;i<=m;i++)
  {
    int u,v,x;
    scanf("%d%d%d",&x,&u,&v);
    if(x==0)//修改u-v路径上所有点权值开根号向下取整
    update_path(u,v);
    else //查询路径u-v上所有节点权值和
    printf("%lld\n",query_path(u,v));
  }
  //system("pause");
  return 0;
}

1005-checkout

思路:

这题代码也是学姐写的,没读题。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 1000005
ll n,m,cnt[N],ans=0,a,b;
int fa[N],color[N];
vector<int> v[N];
map<pair<int,int>,ll> mp;
map<int,int> mpp;
void dfs(int u,int f)
{
	fa[u]=f;
	if(color[u]==color[f]) cnt[u]++;
	for(auto i:v[u])
	{
		if(i==f) continue;
		mp[{u,color[i]}]++;
		if(color[i]==color[u]) cnt[u]++,ans++;
		dfs(i,u);
	}
	mpp.clear();
	for(auto i:v[u]) 
	{
		ll x=mp[{u,color[i]}];
		if(x>0) cnt[i]+=x-1;
		mpp[color[i]]+=x;
	}
	map <int,int>::iterator it;
	for(it=mpp.begin();it!=mpp.end();it++) ans+=it->second/2;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);color[i]=a;
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&a,&b);
		v[a].push_back(b);
		v[b].push_back(a);
	}
	dfs(1,0);
//	cout<<ans<<endl;
//	for(int i=1;i<=n;i++) cout<<i<<": "<<cnt[i]<<endl;
	for(int i=1;i<=n;i++)
	{
		ll maxn=ans-cnt[i];
		for(auto son:v[i])
		{
			ll res=maxn;
			if(son==fa[i]) continue;
			if(color[son]==color[fa[i]]) res++;
			res+=mp[{fa[i],color[son]}];
			if(color[son]==color[i]) res--; 
			maxn=max(maxn,res);
		}
		cout<<maxn;
		if(i<n) cout<<" ";
	}
	cout<<endl;
	return 0;
}

1007-circle

思路:

没读题,应该也是签到,张钰敏写的,但是一开始好像pi精度没弄好wa了,学姐调了一下就过了。

代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double pi=acos(-1);
int main()
{
	int n;
	while(cin>>n)
	{
		double xita=(2*pi)/n;//每个大扇形角度
		double xita2=xita/2;
		double ans=(n-1)*sin(xita)/2;
		ans+=sin(xita2);
		printf("%.6lf\n",ans);
	}
	return 0;
}

1008-Clock

思路:

是一个分类讨论题。

时分秒针的每种组合都会代表两个时刻,因此对于给出的所有时刻我们都计算对应到0-12点时分秒针的组合,只需要计算总的秒数,排序。

由于我们可以进行两种操作,顺时针和逆时针,所以都求之后取最小值。

排序后找起始位置的左右两端的位置,计算差值,用走一圈的度数-差值,最小的即为答案。注意有可能会有和初始时刻重复的时间。

对于初始时刻排序后的位置分类讨论,如果排序后的初始位置在第一位,那么它左端为最后一个;

如果排序后的初始位置在最后一位,那么它左端为第一个。

一发过了,很开心。

代码:

#include <bits/stdc++.h>
const int maxn=9e4;
const int lsum=43200;
using namespace std;
typedef long long ll;
int n;
int h,m,s;
int a[maxn];
int ar,cnt;
double ans;
int suan(int h,int m,int s)
{
	int res=h*60*60+m*60+s;
	if(res>=lsum)return res%lsum;
	else return res;
}
int main()
{
	cin>>n;
	cin>>h>>m>>s;
	a[0]=suan(h,m,s);
	ar=a[0];
	cnt=0;
	for(int i=1;i<=n;i++)
	{
		cin>>h>>m>>s;
		a[i]=suan(h,m,s);
		if(a[i]==ar)cnt++;//标记有几个初始时刻
	}
	sort(a,a+1+n);
	int temp=0;
	for(int i=0;i<=n;i++)
	{
		if(a[i]==ar)
		{
		  temp=i;
		  break;
		}
	}
	int x=0,y=0;
  if(cnt==n)cout<<"0.00"<<endl;
  else
  {
    if(temp==0)
    {
      x=a[n]-a[0];
      y=lsum-(a[0+cnt+1]-a[0]);
      ans=min(x,y);
    }
    else if(temp==n||(temp+cnt)==n)//初始在最后
    {
      x=lsum-(a[temp]-a[temp-1]);
      y=a[temp]-a[0];
      ans=min(x,y);
    }
    else
    {
      x=lsum-(a[temp]-a[temp-1]);
      y=lsum-(a[temp+cnt+1]-a[temp]);
      ans=min(x,y);
    }
  }
	printf("%.2lf\n",ans*6);
  //system("pause");
	return 0;
}


1010-Tangram

思路:

这题也是学姐写的。

七巧板画线,第一次画线多六块,往后每次画线增加的都是之前增加的块数加1;

即:

0:7

1:7+6

2:7+6+7

3:7+6+7+8

......

代码:

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
ll n,t;
int main(){
	while(cin>>n)
	{
		cout<<7+6*n+((n-1)*n)/2<<endl;
	}
	return 0;
}
	

1011-Tetris

思路:

n,m<=12,范围很小,手画了几个感觉只有当n,m是4的倍数时才可以刚好填满。

猜测样例给的4x4是能组成的最小块。

当n,m是4的倍数时,k1=n/4,k2=m/4;输出k1*k2个最小块即可。

注意该题在杭电oj上是多个样例输入。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char a[10][10];
int main()
{
	int n,m;
	a[1][1]='1',a[1][2]='1',a[1][3]='1',a[1][4]='3';
	a[2][1]='2',a[2][2]='1',a[2][3]='3',a[2][4]='3';
	a[3][1]='2',a[3][2]='2',a[3][3]='4',a[3][4]='3';
	a[4][1]='2',a[4][2]='4',a[4][3]='4',a[4][4]='4';
	while(cin>>n>>m)
	{
		if((n%4==0)&&(m%4==0))
		{
			int aa=n/4;
			int bb=m/4;
			for(int i=1;i<=aa;i++)
			{
				for(int h=1;h<=4;h++)
				{
					for(int j=1;j<=bb;j++)
					{                                                                                                                                       
						for(int w=1;w<=4;w++)cout<<a[h][w];
					}
					cout<<endl;
				}
			}
		}
		else cout<<"no response"<<endl;
	}
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值