hgoi#20191031

T1-Divisors

给出 $ m $ 个不同的正整数 $ a_i $ ,设数论函数$ f(k) = \sum\limits_{i = 1}^{n} [(\sum\limits_{j = 1}^{m} [i |a_j] )== k] $
其中 $ a | b $ 表示 $ a $ 是 $ b $ 的因数,对于所有 $ k \in [0,m] $ ,输出答案。
对于100%的数据满足 $ n\leq 200 , a_i \leq 10^9 $

解法

这道题直接暴力算每个数的贡献
因为一个数的因数个数并不多,一定不超过$ \sqrt n $个
最劣也是 $ n \sqrt {a_i} $ 个数,完全存的下

ac代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
map<int,int>mp;
int n,m,cnt,x,k,p[200010],ans[210];
int main()
{
	freopen("div.in","r",stdin);
	freopen("div.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&x),k=sqrt(x);
		for(int i=1;i<=k;i++)if(x%i==0)
		{
			if(mp[i]==0&&i<=n)mp[i]=++cnt;
			if(i<=n)p[mp[i]]++;
			if(mp[x/i]==0&&x/i<=n)mp[x/i]=++cnt;
			if(x/i<=n)p[mp[x/i]]++;
		}
		if(k*k==x&&k<=n)p[mp[k]]--;
	}
	for(int i=1;i<=cnt;i++)ans[p[i]]++;
	printf("%d\n",n-cnt);
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}

T2-Marke

有 $ n $ 个商店,每个商店某一时刻 $ r_i $ 及以后会卖 $ 1 $ 种物品且只有 $ 1 $ 个。
每个物品有花费 $ c_i $ 和价值 $ v_i $ ,处理 $ m $ 个询问,表示在 $ t_i $ 时刻购买所有物品且有钱数 $ w_i $ 。
输出当前限制的最大价值。
对于100%的数据满足 $ 1 \leq n \leq 300, 1 \leq m \leq 10^5, 1 \leq c_i,w_i \leq 10^9, 1 \leq r_i , t_i \leq 300 $

解法

dp,设 $ f_i $ 表示价值达到i需要的最少花费
暴力更新的复杂度是 $ n^3 $
以时间为轴去更新
如果当前时间点有询问
由于f[i]具有单调性,所以二分答案

ac代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define mid (l+r>>1)
#define inf 0x3f3f3f3f
using namespace std;
struct node
{
	int c,v,t;
	void init(){scanf("%d%d%d",&c,&v,&t);}
	bool operator<(const node&a)const{return t<a.t;}
}a[310];
struct Node
{
	int id,t,m;
	void init(int i){id=i,scanf("%d%d",&t,&m);}
	bool operator<(const Node&a)const{return t<a.t;}
}q[100010];
int n,m,maxx,maxm,f[100010],ans[100010];
//f[i]表示i价值最少要多少钱 
void upd(int&x,int y){x=min(x,y);}
int main()
{
//	freopen("market.in","r",stdin);
//	freopen("market.out","w",stdout);
	memset(f,0x3f,sizeof(f)),f[0]=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)a[i].init();
	for(int i=1;i<=m;i++)q[i].init(i),maxm=max(maxm,q[i].m);
	sort(a+1,a+n+1),sort(q+1,q+m+1);
	for(int i=1,x=1,y=1;i<=n;i++)
	{
		while(a[x].t==i)
		{
			for(int j=90000;j>=0;j--)
			{
				if(f[j]+a[x].c>maxm)continue;
				upd(f[j+a[x].v],f[j]+a[x].c);
			}
			for(int j=90000;j>=1;j--)
				upd(f[j-1],f[j]);
			x++;
		}
		while(q[y].t==i)
		{
			int l=0,r=90000,res;
			while(l<=r)
				if(f[mid]<=q[y].m)res=mid,l=mid+1;
				else r=mid-1;
			ans[q[y].id]=res,y++;
		}
	}
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}

T3-Dash Speed

一棵含有 $ n $ 个节点的树,每一条无向边都有一个区间 $ [l_i , r_i] $
给出 $ m $ 个询问,每次输入一个 $ x $ ,设树上一条 $ k $ 条边的路径,$ p_1 , p_2 , ... ,p_k $
求 $ x \in \cap {i = 1}^{k} [l, r_{p_i}] $ ,最大化 $ k $ 的值。
对于100%的数据满足 $ 1 \leq n ,m\leq 7\times 10^4 , 1 \leq l_i \leq r_i \leq n,1 \leq x \leq n $

解法

考虑转化这个问题
就是要维护一个动态树的直径
对于增边操作,新树的直径的端点一定是两棵原树的直径的端点
并查集搞一下就可以做到
对于删边操作,有点难搞

显然要先预处理出所有答案,再在线询问
对询问进行分治(类似整体二分)
如果当前处理的区间为 $ [l,r] $
那么它可以经过所有承受区间包含 $ [l,r] $ 的边
把这些边加入并合并,然后继续分治
因为两边都要去分治,所以需要回溯操作
那么并查集就不能路径压缩,而是按秩合并
回溯时撤销之前的操作即可
如果用欧拉序和ST表求LCA
复杂度可以达到 $ n \log n $

ac代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define mid (l+r>>1)
#define ls (nw<<1)
#define rs (nw<<1|1)
#define N 70010
#define M 140010
#define P 280010
#define K 20
using namespace std;
vector<int>e[N];
vector<pair<int,int> >g[P];
struct node{int t,x,y;}u;
stack<node>q;
int n,m,a,b,c,d,t,tmp,cnt;
int p[K],f[N],A[N],B[N],dep[N],depth[N];
int fst[N],ans[N],oula[M],lg[M],ST[M][K];
void ins(int nw,int l,int r,int ql,int qr,int x,int y)
{
    if(ql<=l&&r<=qr){g[nw].pb(mp(x,y));return;}
    if(ql<=mid)ins(ls,l,mid,ql,qr,x,y);
    if(qr>mid)ins(rs,mid+1,r,ql,qr,x,y);
}
void dfs(int u,int fa)
{
    f[u]=A[u]=B[u]=u,oula[++cnt]=u,fst[u]=cnt;
    for(auto v:e[u])if(v!=fa)
        dep[v]=dep[u]+1,dfs(v,u),oula[++cnt]=u;
}
void init()
{
    for(int i=1;i<=cnt;i++)ST[i][0]=oula[i];
    for(int j=1;j<=lg[cnt];j++)for(int i=1;i+p[j]-1<=cnt;i++)
        if(dep[ST[i][j-1]]<dep[ST[i+p[j-1]][j-1]])
			ST[i][j]=ST[i][j-1];
        else ST[i][j]=ST[i+p[j-1]][j-1];
}
int lca(int x,int y)
{
    if(x==y)return x;
    x=fst[x],y=fst[y];
    if(x>y)swap(x,y);
    if(dep[ST[x][lg[y-x]]]<dep[ST[y-p[lg[y-x]]+1][lg[y-x]]])
		return ST[x][lg[y-x]];
    else return ST[y-p[lg[y-x]]+1][lg[y-x]];
}
int find(int x){return x==f[x]?x:find(f[x]);}
int dis(int x,int y){return dep[x]+dep[y]-dep[lca(x,y)]*2;}
void merge(int x,int y,int&res)
{
    x=find(x),y=find(y),t=-1;
    tmp=dis(A[x],B[x]);
    if(tmp>t)t=tmp,a=A[x],b=B[x];
    tmp=dis(A[x],A[y]);
    if(tmp>t)t=tmp,a=A[x],b=A[y];
    tmp=dis(A[x],B[y]);
    if(tmp>t)t=tmp,a=A[x],b=B[y];
    tmp=dis(B[x],A[y]);
    if(tmp>t)t=tmp,a=B[x],b=A[y];
    tmp=dis(B[x],B[y]);
    if(tmp>t)t=tmp,a=B[x],b=B[y];
    tmp=dis(A[y],B[y]);
    if(tmp>t)t=tmp,a=A[y],b=B[y];
    if(res<t)res=t;
    if(depth[x]==depth[y])depth[x]++,q.push({0,x,0});
    if(depth[x]<depth[y])swap(x,y);
    q.push({1,y,0}),q.push({2,x,A[x]}),q.push({3,x,B[x]}),
	f[y]=x,A[x]=a,B[x]=b;
}
void split(int pos)
{
    while(q.size()>pos)
    {
        u=q.top(),q.pop();
        if(u.t==0)depth[u.x]--;
        if(u.t==1)f[u.x]=u.x;
        if(u.t==2)A[u.x]=u.y;
        if(u.t==3)B[u.x]=u.y;
    }
}
void solve(int nw,int l,int r,int res)
{
    int pos=q.size();
    for(auto x:g[nw])merge(x.fir,x.sec,res);
    if(l==r)ans[l]=res;
    else solve(ls,l,mid,res),solve(rs,mid+1,r,res);
    split(pos);
}
int main()
{
    scanf("%d%d",&n,&m),lg[0]=-1;
    for(int i=1;i<M;i++)lg[i]=lg[i>>1]+1;
    for(int i=0;i<K;i++)p[i]=(1<<i);
    for(int i=1;i<n;i++)
		scanf("%d%d%d%d",&a,&b,&c,&d),
		e[a].pb(b),e[b].pb(a),ins(1,1,n,c,d,a,b);
    dfs(1,0),init(),solve(1,1,n,0);
    while(m--)scanf("%d",&a),printf("%d\n",ans[a]);
    return 0;
}
posted @ 2019-10-31 18:10  可耐滴小慕容  阅读(114)  评论(0编辑  收藏  举报