AtCoder Beginner Contest 294(E,F,G)
AtCoder Beginner Contest 294(E,F,G)
E (思维,双指针)
这个题的大意就是有一个\(2\)行\(L\)列的网格,每个格子里面都有不同的数字,但是它的输入方式不是一个一个输入的,而是从第一个开始,枚举每个数在这一段的数量,(比如\(a_1=2,L_1=3\),那么此时就已经有了一个长度为\(3\)的数列(\(2 2 2\))了
我们要求的是第一行和第二行相同的数的数量
一个一个去找显然是不太可能(\(L\)的范围有一点大),但是题目告知每行的段的数量只有\(1e5\)的范围,而且每一行都是从头开始,都是有序的,那么我们可以定义两个指针,一个指向第一行上一次的还未使用完的段,一个是指向第二行的一段,只要这两个指针指向的那一个段里面的值一样,那么就可以获得这两个段的交集的数量的贡献,就算是不一样的,但是第二行的指针指向的线段还是需要减少的,(这个交集里面的数一定是不一样的,贡献一定为\(0\))
这样的操作两个指针最多会到\(1e5\),不会超时
具体看代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include<cmath>
#include <bitset>
#include <unordered_map>
using namespace std;
const int maxn=4e5+10;
const int mod=998244353;
const double eps=1e-7;
#define int long long
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
int n,m,L;
int a[maxn],b[maxn];
signed main ()
{
cin>>L>>n>>m;
int res=0;
for (int i=1;i<=n;i++)
{
cin>>a[i]>>b[i];
}
int now=1;
for (int i=1;i<=m;i++)
{
int v,l;
cin>>v>>l;
while (l)
{
int len=min(l,b[now]);
if(v==a[now]) res+=len;
b[now]-=len;
l-=len;
if(!b[now]) now++;
}
}
cout<<res<<"\n";
system ("pause");
return 0;
}
F(二分)
这个题目大意是有两个人,他们各自有\(n\)和\(m\)种配料组合(\(x\)和\(y\)),现在我们要让这两个人从中选择出任意一种组合和另外一个搭配,搭配后的浓度公式如下
求第\(k\)高的浓度大小为多少
对于答案,我们知道一定是在\(0\)和\(1\)之间寻找
所以我们可以用到二分,但是\(check\)函数该怎么写呢
假设此时的第\(k\)大的浓度为\(mid\)
那么我们可以发现存在\(k-1\)个这样的式子\(\frac{x_i+x_j}{x_i+x_j+y_i+y_j}>mid\)
一个一个寻找还是不太好
我们可以试着把上面那个式子移项,把\(i\)的\(x\)和\(y\)移到一边
变成了
\((1-mid)\times x_i-mid\times y_i>(mid-1)\times x_j+mid\times y_j\)
然后求这样的式子有多少个,我们可以分别存下大于号左右两边的式子,排序,然后和上一题类似的求出满足以上式子的数量,记录为\(cnt\)
如\(cnt>k\),那么这个一定是不满足的,浓度低了
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include<cmath>
#include <bitset>
#include <unordered_map>
using namespace std;
const int maxn=4e5+10;
const int mod=998244353;
const double eps=1e-12;
#define int long long
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
int n,m,k;
double x[maxn],y[maxn],xx[maxn],yy[maxn];
bool check(double now)
{
vector<double>a(n+1),b(m+1);
for (int i=1;i<=n;i++)
{
a[i]=(1.0-now)*x[i]-now*y[i];
}
for (int i=1;i<=m;i++)
{
b[i]=(now-1.0)*xx[i]+now*yy[i];
}
sort(a.begin()+1,a.end());
sort(b.begin()+1,b.end());
int cur=1,cnt=0;
for (int i=1;i<=n;i++)
{
while (cur<=m&&b[cur]<a[i])
{
cur++;
}
cnt+=(cur-1);
if(cnt>k) return false;
}
return cnt<=k;
}
signed main ()
{
cin>>n>>m>>k;
for (int i=1;i<=n;i++)
{
cin>>x[i]>>y[i];
}
for (int i=1;i<=m;i++)
{
cin>>xx[i]>>yy[i];
}
double l=0,r=1.0;
k--;
double ans;
while (r-l>eps)
{
double mid=(l+r)/2.0;
if(check(mid))
{
r=mid;
ans=mid;
}
else l=mid;
}
ans*=100.0;
printf("%.10lf\n",ans);
system ("pause");
return 0;
}
G(lca,树状数组)
这个题首先是给你\(n-1\)条边,每一条边都会有一个权值,然后会有\(q\)个操作,有两种操作类型
\(1\),输入\(1,i,w\),代表把第\(i\)条边的权值更新为\(w\)
\(2\),输入\(2,x,y\),需要我们输出这两个点的路径的权值和
假如没有修改权值的话,我们或许可以直接\(dis(x)+dis(y)-dis(lca(x,y))\),但是还要修改里面的边的值,每次都要进行\(dfs\)就很不现实,故我们可采用树状数组,把每一个点对这些路径的贡献都转化为\(dfs\)的遍历顺序存放在树状数组里面
如何记录每一个点的影响范围呢
我们可以记录每一个点的\(dfn1\)序和一另外一个\(dfn2\)序,在\(dfn1和dfn2\)这一段里面,就是这一个点的贡献
然后对于修改一条边的权值,我们要怎样修改呢
我们可以先这样看,从\(1\)到\(u\)的距离为\(dis\),而\(u\)到\(v\)的距离为\(val\),那么从\(1\)到\(v\)的距离为\(dis+val\),所以我们可以改变\((u,v)\)这一条边的值只会影响\(v\)所连接的路径,而不会影响到\(u\)
所以我们只需要改变\(v\)点的影响范围的值即可,还要记得更新边的值,防止下一次又是更新这一条边,但是这里面存的不是最新的值
然后\(lca\)我这里用了倍增写法,可以参考模板
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include<cmath>
#include <bitset>
#include <unordered_map>
using namespace std;
const int maxn=2e5+10;
const int mod=998244353;
const double eps=1e-12;
#define int long long
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
struct node
{
int next,to,val,id;
}e[maxn<<2];
int bit[maxn<<2];
int head[maxn],cnt;
int dfn1[maxn],dfn2[maxn],f[maxn][31];
int idx[maxn];
int dep[maxn];
int u[maxn],v[maxn],val[maxn];
int tim;
int s=30;
int n,q;
void add(int u,int v,int val,int id)
{
e[++cnt].next=head[u];
e[cnt].to=v;
e[cnt].val=val;
e[cnt].id=id;
head[u]=cnt;
return ;
}
void dfs(int u,int fa)
{
dfn1[u]=++tim;
f[u][0]=fa;
for (int i=1;i<s;i++)
{
f[u][i]=f[f[u][i-1]][i-1];
}
for (int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
int id=e[i].id;
dep[v]=dep[u]+1;
dfs(v,u);
idx[id]=v;
}
dfn2[u]=tim;
return ;
}
int lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
int dlt=dep[u]-dep[v];
for (int i=0;i<s;i++)
{
if((dlt>>i)&1)u=f[u][i];
}
if(u==v) return u;
for (int i=s-1;i>=0;i--)
{
if(f[u][i]!=f[v][i])
{
u=f[u][i];
v=f[v][i];
}
}
return f[u][0];
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int val)
{
while (x<=n)
{
bit[x]+=val;
x+=lowbit(x);
}
return ;
}
int query(int x)
{
int res=0;
while (x)
{
res+=bit[x];
x-=lowbit(x);
}
return res;
}
signed main ()
{
cin>>n;
for (int i=1;i<n;i++)
{
cin>>u[i]>>v[i]>>val[i];
add(u[i],v[i],val[i],i);
add(v[i],u[i],val[i],i);
}
dfs(1,0);
for (int i=1;i<n;i++)
{
update(dfn1[idx[i]],val[i]);
update(dfn2[idx[i]]+1,-val[i]);
}
cin>>q;
while (q--)
{
int op,x,y;
cin>>op>>x>>y;
if(op==1)
{
int now=idx[x];
update(dfn1[now],y-val[x]);
update(dfn2[now]+1,val[x]-y);
val[x]=y;
}
else
{
// cout<<"ans ";
int res=query(dfn1[x])+query(dfn1[y])-2*query(dfn1[lca(x,y)]);
cout<<res<<"\n";
}
}
system ("pause");
return 0;
}

浙公网安备 33010602011771号