北京Day 3

今天T3数据出锅了,所有正解、非正解最高得分10分,终于赶上大佬们的分了。

今日得分:100+100+10(其实是非正解70)

T1 luoguP1903

题目大意:给你一串长度为N的序列,需要支持M个操作:1.查询L到R中不同数字的个数。2.修改某一位数字

n,m<=50000

题解:有大佬写树套树,然而我并不会,好在带修莫队算法可以过。

将所有询问操作按照左端点所在块,右端点所在块,时间戳为第一第二第三关键字排序,然后对于每次询问暴力算即可。

T1AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#pragma GCC optimize(2)
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
inline char read()
{
	char ch=getchar();while(ch!='Q'&&ch!='R')ch=getchar();return ch;
}
int n,m,a[500010],b[500010],kdx,kgs,num[1000010];
struct wt{
	int l,r,lb,rb,ii,tim,ans;
}qu[500010];
struct xg{int x,las,num;}change[500010];
inline bool cmp(wt x,wt y)
{if(x.lb!=y.lb)return x.lb<y.lb;if(x.rb!=y.rb)return x.rb<y.rb;return x.tim<y.tim;}
int  main()
{
	register int l=0,r=0,cnt=0,cnt2=0,ncha=0,ans=0;
	register char op;
	n=re_ad();m=re_ad();
	for(int i=1;i<=n;i++)a[i]=re_ad(),b[i]=a[i];
	kdx=(int)sqrt(n)+1;
	for(int i=1;i<=m;i++)
	{
	op=read();
	if(op=='Q'){qu[++cnt].l=re_ad();qu[cnt].r=re_ad();qu[cnt].ii=cnt;qu[cnt].tim=cnt2;qu[cnt].lb=(qu[cnt].l+kdx-1)/kdx;qu[cnt].rb=(qu[cnt].r+kdx-1)/kdx;}
	else if(op=='R'){change[++cnt2].x=re_ad();change[cnt2].num=re_ad();change[cnt2].las=b[change[cnt2].x];b[change[cnt2].x]=change[cnt2].num;}
	}
	sort(qu+1,qu+cnt+1,cmp);
	{
	l=qu[1].l;r=qu[1].r;
	for(int i=l;i<=r;i++){if(!num[a[i]])++ans;++num[a[i]];}
	while(ncha<qu[1].tim)
	{++ncha;
	if(change[ncha].x>=l&&change[ncha].x<=r)
	{--num[a[change[ncha].x]];if(!num[a[change[ncha].x]])--ans;}
	a[change[ncha].x]=change[ncha].num;
	if(change[ncha].x>=l&&change[ncha].x<=r)
	{if(!num[a[change[ncha].x]])++ans;++num[a[change[ncha].x]];}
	}
	qu[1].ans=ans;
	}
	for(register int i=2;i<=cnt;++i)
	{
	while(ncha<qu[i].tim)
	{++ncha;
	if(change[ncha].x>=l&&change[ncha].x<=r)
	{--num[a[change[ncha].x]];if(!num[a[change[ncha].x]])--ans;}
	a[change[ncha].x]=change[ncha].num;
	if(change[ncha].x>=l&&change[ncha].x<=r)
	{if(!num[a[change[ncha].x]])++ans;++num[a[change[ncha].x]];}
	}
	while(ncha>qu[i].tim)
	{
	if(change[ncha].x>=l&&change[ncha].x<=r)
	{--num[a[change[ncha].x]];if(!num[a[change[ncha].x]])--ans;}
	a[change[ncha].x]=change[ncha].las;
	if(change[ncha].x>=l&&change[ncha].x<=r)
	{if(!num[a[change[ncha].x]])++ans;++num[a[change[ncha].x]];}
	--ncha;
	}
	while(l>qu[i].l){--l;if(!num[a[l]])++ans;++num[a[l]];}
	while(l<qu[i].l){--num[a[l]];if(!num[a[l]])--ans;++l;}
	while(r<qu[i].r){++r;if(!num[a[r]])++ans;++num[a[r]];}
	while(r>qu[i].r){--num[a[r]];if(!num[a[r]])--ans;--r;}
	qu[i].ans=ans;
	}
	for(int i=1;i<=cnt;i++)b[qu[i].ii]=qu[i].ans;
	for(int i=1;i<=cnt;i++)printf("%d\n",b[i]);
}

T2 luoguP2634

题目大意:给定一棵有边权的树,求任选两点(有序),两点之间的路径边权和是3的倍数的概率。n<=20000

题解:之前见过这道题,显然的点分治,然而自己并没有写过。考场上写的是上限n^2的做法,(然而n<=20000还给2s时限,说过就过)。

其实这道题还可以用树dp做(然而dp弱得一塌糊涂的我并没敢写)

n^2做法:枚举每一个点作为LCA对答案的贡献,乱搞一下就出来了。

点分治做法:每次从当前树的重心出发,处理所有通过重心的路径,再递归求解子树。统计答案容斥一下就好了(详见代码)。

T2AC代码(n^2)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#pragma GCC optimize(2)
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
int gcd(int x,int y){return (y)?gcd(y,x%y):x;}
int n,ans=0;
struct e {int to,cost;};
struct yu{int yus[3];};
vector<e> g[20010];
yu dfs(int x,int fa)
{
	register int sz=g[x].size();
	register yu ret,y,yy;
	vector<yu> z;
	ret.yus[1]=ret.yus[2]=0;ret.yus[0]=0;
	for(register int i=0;i<sz;++i)
	{
	if(g[x][i].to==fa)continue;
	y=dfs(g[x][i].to,x);
	yy.yus[(1+g[x][i].cost)%3]=y.yus[1];yy.yus[(2+g[x][i].cost)%3]=y.yus[2];yy.yus[(g[x][i].cost)%3]=y.yus[0];
	z.push_back(yy);
	ret.yus[1]+=yy.yus[1];ret.yus[2]+=yy.yus[2];ret.yus[0]+=yy.yus[0];
	}
	ans+=ret.yus[0]<<1;++ret.yus[0];
	if(fa)--sz;
	for(register int i=0;i<sz;++i)for(register int j=i+1;j<sz;++j){ans+=(z[i].yus[1]*z[j].yus[2]<<1)+(z[i].yus[2]*z[j].yus[1]<<1)+(z[i].yus[0]*z[j].yus[0]<<1);}
	//cout<<x<<" "<<ans<<" "<<ret.yus[0]<<" "<<ret.yus[1]<<" "<<ret.yus[2]<<endl;
	return ret;
}
int  main()
{
	register int x,y,z;
	n=re_ad();
	for(int i=1;i<n;i++)
	{
	x=re_ad();y=re_ad();z=re_ad();
	g[x].push_back((e){y,z});g[y].push_back((e){x,z});
	}
	dfs(1,0);ans+=n;
	n*=n;
	x=gcd(ans,n);ans/=x;n/=x;
	printf("%d/%d\n",ans,n);
}

T2AC代码(点分治)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#pragma GCC optimize(2)
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
int gcd(int x,int y){return (y)?gcd(y,x%y):x;}
int n,ans=0,sum,son[20010],yus[5],size[20010],d[20010],root;
bool vis[20010];
struct e {int to,cost;};
vector<e> g[20010];
void getroot(int x,int fa)
{
	register int sz=g[x].size(),v;
	size[x]=1;son[x]=0;
	for(register int i=0;i<sz;i++)
	{
	v=g[x][i].to;
	if(vis[v]||v==fa)continue;
	getroot(v,x);
	size[x]+=size[v];son[x]=max(son[x],size[v]);
	}
	son[x]=max(son[x],sum-son[x]);
	if(son[x]<son[root])root=x;
}
void getnum(int x,int fa)
{
	register int sz=g[x].size(),v;
	++yus[d[x]];
	for(register int i=0;i<sz;i++)
	{
	v=g[x][i].to;
	if(vis[v]||v==fa)continue;
	d[v]=(d[x]+g[x][i].cost)%3;
	getnum(v,x);
	}
}
inline int gx(int u,int num)
{
	d[u]=num%3;yus[1]=yus[2]=yus[0]=0;
	getnum(u,0);
	return yus[0]*yus[0]+(yus[1]*yus[2]<<1);
}
void dfs(int x)
{
	register int sz=g[x].size(),v;
	vis[x]=true;ans+=gx(x,0);
	for(register int i=0;i<sz;i++)
	{
	v=g[x][i].to;
	if(vis[v])continue;
	ans-=gx(v,g[x][i].cost);
	sum=size[v];root=0;
	getroot(v,x);
	dfs(root);
	}
}
int  main()
{
	register int x,y,z;
	n=re_ad();
	for(int i=1;i<n;i++)
	{
	x=re_ad();y=re_ad();z=re_ad();
	g[x].push_back((e){y,z});g[y].push_back((e){x,z});
	}
	sum=n;son[0]=n;
	getroot(1,0);
	dfs(1);
	n*=n;
	x=gcd(ans,n);ans/=x;n/=x;
	printf("%d/%d\n",ans,n);
}

T3 luoguP4409

题目大意:一个环形序列,要求在每个位置上放置P[i]个数,要求相邻两个位置不能有数相同,求最少需要放的数的个数。

表示老师今天讲的二分方法(虽然并没有听懂)是错的,也因此标程是错的,连带着数据也是错的。

正解有两个,一个是二分(详见洛谷题解),一个是贪心。

这里讲一下贪心做法:

首先,对于相邻两个数来说,答案一定大于二者之和。所以答案1就是相邻两数的和的最大值。

但是这样做有一个问题:当n是奇数时,最后一个和第一个之间会有重复,导致答案错误。

对于这种情况,我们换个角度考虑,每个勋章最多给n/2个将军,所以实际上sum(a[i])/(n/2)的勋章足以满足题设条件,向上取整,与答案1取max就可以过了。

T3AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#pragma GCC optimize(2)
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
int n,P[20010],ans;long long sum;
inline int ma(int x,int y){return x>y?x:y;}
int  main()
{
	n=re_ad();
	for(int i=1;i<=n;i++)P[i]=re_ad(),sum+=P[i];P[n+1]=P[1];
	for(register int i=1;i<=n;i++){ans=ma(ans,P[i]+P[i+1]);}
	if(n&1)
	{
	ans=ma(ans,(sum+n/2-1)/(n/2));
	}
	cout<<ans<<endl;
	return 0;
}

总结

暴力千万条,分治第一条,

分块不卡常,血亏两行泪。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值