今天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;
}