CCPC-Wannafly Winter Camp Day5 Div2 部分题解

本来这几天单挑ccpc camp div2很爽,直到今天遇到了计算几何..............

A.Cactus Draw

签到题

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1005;
vector<int>G[maxn];
struct node
{
	int x,y;
}ans[maxn];
int vis[maxn],dep[maxn];
void dfs(int u,int fa,int x,int y)
{
	ans[u].x=x,ans[u].y=y;
	vis[u]=0;
	int cnt=0;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa||vis[v])continue;
		dep[x]++;
		dfs(v,u,x+1,dep[x]);
	}
}
int main()
{
	int n,m,u,v;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0,1,1);
	for(int i=1;i<=n;i++)
	printf("%d %d\n",ans[i].x,ans[i].y);
}

C.Division

签到题,优先队列就好了,手残写了线段树

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
ll mx[maxn*4],sum[maxn*4];
void pushup(int o,int ls,int rs)
{
	mx[o]=max(mx[ls],mx[rs]);
	sum[o]=sum[ls]+sum[rs];
}
void build(int o,int l,int r)
{
	if(l==r)
	{
		scanf("%lld",&mx[o]);
		sum[o]=mx[o];
		return;
	}
	int m=(l+r)/2,ls=o*2,rs=o*2+1;
	build(ls,l,m);
	build(rs,m+1,r);
	pushup(o,ls,rs);
}
void up(int o,int l,int r)
{
	if(l==r)
	{
		mx[o]=sum[o]=mx[o]/2;
		return;
	}
	int m=(l+r)/2,ls=o*2,rs=o*2+1;
	if(mx[ls]>=mx[rs])up(ls,l,m);
	else up(rs,m+1,r);
	pushup(o,ls,rs);	
}
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	build(1,1,n);
	for(int i=1;i<=k;i++)
	{
		up(1,1,n);
		if(mx[1]==0)break;
	}
	printf("%lld\n",sum[1]);
}

F.Kropki

 

设d[i][s][j] 是在第 i 个位置时,集合为s,结尾元素是 j 的方案数,我们由第 i 个位置推第 i+1 个位置,我们枚举第 i+1 个位置的元素 p,再枚举 j,如果p 和 j 的取值合法,那么d[ i+1 ][ s|(1<<p-1)][ p ]+=d[ i ][s][ j ]。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll d[2][1<<16][16],ans;
int cur,n;
char str[20];
int ss(int x)
{
	int res=0;
	while(x)
	{
		if(x&1)res++;
		x/=2;
	}
	return res;
}
void add(ll& x,ll y)
{
	x=(x+y)%mod;
}
void clear(int cur,int v)
{
	for(int i=0;i<(1<<n);i++)
	for(int j=1;j<=n;j++)d[cur][i][j]=v;
}
int main()
{
	cin>>n>>str+1;
	clear(cur,1);
	for(int i=1;i<n;i++)
	{
		cur=!cur;
		clear(cur,0);
		for(int j=1;j<(1<<n);j++)
		if(ss(j)==i)
		{
			for(int k=1;k<=n;k++)
			if(!((1<<k-1)&j))
			{
				int s=(j|(1<<k-1));
				for(int p=1;p<=n;p++)
				if((1<<p-1)&j)
				{
					if(str[i]=='0'&&(p*2==k||k*2==p))continue;
					if(str[i]=='1'&&(p*2!=k&&k*2!=p))continue;
					add(d[cur][s][k],d[!cur][j][p]);
				}
			}
		}
	}
	for(int i=1;i<=n;i++)
	add(ans,d[cur][(1<<n)-1][i]);
	cout<<ans;
}

H.Nested Tree

我们先把所有的树都连接起来(1e6个点),然后我们树形dp一下,假设1为根,我们从u走到了v,边u->v的贡献是多少呢?设d[ i ]为以 i 为根的子树的所有节点个数,那么u那边一共有nm-d[v]个点,v这边一共有d[v]个点,显然边u->v的贡献是(nm-d[v])*d[v]。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1005,mod=1e9+7;
vector<int>G[1000001];
int d[maxn*maxn],n,m;
ll ans=0;
void dfs(int u,int fa)
{
	d[u]=1;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa)continue;
		dfs(v,u);
		d[u]+=d[v];
	}
	ans=(ans+1ll*d[u]*(n*m-d[u])%mod)%mod;
}
int main()
{
	int a,b,u,v;
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		for(int j=1;j<=m;j++)
		{
			int uu=u+(j-1)*n;
			int vv=v+(j-1)*n;
			G[uu].push_back(vv);
			G[vv].push_back(uu);
		}
	}
	for(int i=1;i<m;i++)
	{
		scanf("%d%d%d%d",&a,&b,&u,&v);
		int uu=u+(a-1)*n;
		int vv=v+(b-1)*n;
		G[uu].push_back(vv);
		G[vv].push_back(uu);
	}
	dfs(1,0);
	printf("%lld\n",ans);
}

I.Sorting

被计算几何卡的我时间全用完,看了题解发现这题好简单,由于不能补题,先不写这题,等开放补题再来写。

update:赛后1a了这个题,我们发现不管是执行操作2还是操作3,比不大于x的数相对位置不变,大于x的数也是一样,那么我们把所有不大于x的数看做0,大于x的数看做1,并分别求出这两类数前缀和,把01插入到线段树,操作:2 l r,我就把区间 l r的所有的1移到区间右边去,操作:3 l r,我就把区间 l r 所有的1移到区间左边去,怎么查询 L R 区间和?。我们先找到区间[ 1 , R ]的1个数y,找到区间[ 1 , L-1 ] 的1的个数x,用大于x那类数的前缀和sum[y]-sum[x]就求出来区间[ L , R ]大于x的所有数之和了,不大于x的区间和同理。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll sum1[maxn],sum2[maxn],ans;
int sum[maxn*4],tag[maxn*4],a[maxn],x,cnt1,cnt2;
void pushdown(int o,int ls,int rs,int l,int r,int m)
{
	if(tag[o]!=-1)
	{
		sum[o]=(r-l+1)*tag[o];
		tag[ls]=tag[rs]=tag[o];
		sum[ls]=tag[o]*(m-l+1);
		sum[rs]=tag[o]*(r-m);
		tag[o]=-1;
	}
}
void up(int o,int l,int r,int ql,int qr,int v)
{
	int m=(l+r)/2,ls=o*2,rs=o*2+1;
	if(l!=r)
	pushdown(o,ls,rs,l,r,m);
	if(l>=ql&&r<=qr)
	{
		tag[o]=v;
		sum[o]=v*(r-l+1);
		return;
	}
	if(ql<=m)up(ls,l,m,ql,qr,v);
	if(qr>m)up(rs,m+1,r,ql,qr,v);
	sum[o]=sum[ls]+sum[rs];
}
int qu(int o,int l,int r,int ql,int qr)
{
	if(qr<ql)return 0;
	int m=(l+r)/2,ls=o*2,rs=o*2+1;
	if(l!=r)
	pushdown(o,ls,rs,l,r,m);
	if(l>=ql&&r<=qr)return sum[o];
	int res=0;
	if(ql<=m)res+=qu(ls,l,m,ql,qr);
	if(qr>m)res+=qu(rs,m+1,r,ql,qr);
	return res;
}
int main()
{
	int n,q,op,l,r;
	scanf("%d%d%d",&n,&q,&x);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]<=x)
		sum1[++cnt1]=sum1[cnt1-1]+a[i];
		else
		{
			sum2[++cnt2]=sum2[cnt2-1]+a[i];
			up(1,1,n,i,i,1);
		}
	}
	while(q--)
	{
		scanf("%d%d%d",&op,&l,&r);
		if(op==1)
		{
			int y=qu(1,1,n,1,r);
			int x=qu(1,1,n,1,l-1);
			ans=sum2[y]-sum2[x];
			y=r-y;
			x=l-1-x;
			ans+=sum1[y]-sum1[x];
			printf("%lld\n",ans);
		}
		else if(op==2)
		{
			int v=r-l+1-qu(1,1,n,l,r);
			up(1,1,n,l,l+v-1,0);
			up(1,1,n,l+v,r,1);
		}
		else
		{
			int v=qu(1,1,n,l,r);
			up(1,1,n,l,l+v-1,1);
			up(1,1,n,l+v,r,0);
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值