Educational Codeforces Round 58 (Rated for Div. 2) D、F

D

题意

一棵树。
求最长两点路径,并且路径上所有的 g c d > 1 gcd>1

题解

可以考虑 d p dp ,因为 2 3 5 7 11 13 17 19 > 1 e 5 2*3*5*7*11*13*17*19>1e5
所有每个点的值最大质因数不超过 8 8 个。算 10 10 个。
d p [ i ] [ j ] dp[i][j] 表示从 i i 开始到子树内的最大长度。
d p [ i ] [ j ] = d p [ s o n ] [ k ] + 1 dp[i][j]=dp[son][k]+1 k k 是子结点值的因数中 v a l val 的位置, v a l val i i 的第 j j 个质因数。
但显然这样是不行的,会因为存在两个不在一条链上(我们在 d p dp 过程人为规定了直链),所以对 i i ,要处理出子结点 d p dp 值最大两个,相加再加上第 i i 个结点更新答案。
一共就这两种情况。

#include <bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define RFOR(i,a,b) for(int i=a;i>=b;i--)
#define sf(x) scanf("%d",&x)
#define inf 0x3f3f3f3f
using namespace std;
 
typedef long long ll;
const int maxn = 200050+500;
const ll mod = 1e9+7;
 
int dp[maxn][10];
map<pair<int,int>,int>mp;
vector<int>ver[maxn],G[maxn];
int A[maxn],ans=0;
int Mx1[20],Mx2[20];
 
void dfs(int u,int f){
    for(auto v:G[u]){
        if(v==f)continue;
        dfs(v,u);
    }
    for(int i=1;i<=10;i++)Mx1[i]=Mx2[i]=0;
    for(int i=1;i<=ver[u].size();i++){
        dp[u][i]=1;
        for(int j=0;j<G[u].size();j++){
            int v=G[u][j];
            if(v==f)continue;
            int pos=mp[make_pair(A[v],ver[u][i-1])];
            dp[u][i]=max(dp[u][i],dp[v][pos]+1);
            if(dp[v][pos]>=Mx1[i])Mx2[i]=Mx1[i],Mx1[i]=dp[v][pos];
            else if(dp[v][pos]>Mx2[i])Mx2[i]=dp[v][pos];
        }
    }
    for(int i=1;i<=ver[u].size();i++){
        ans=max(ans,dp[u][i]);
        ans=max(ans,Mx1[i]+Mx2[i]+1);
    }
}
 
int main(){
    int n;cin>>n;
    FOR(i,1,n)sf(A[i]);
    FOR(i,1,n-1){
        int u,v;
        sf(u),sf(v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1;i<=n;i++){
        int val=A[i];
        for(int j=2;j<=sqrt(val);j++){
            if(val%j==0){
                ver[i].push_back(j);
                while(val%j==0)val/=j;
                mp[make_pair(A[i],j)]=ver[i].size();
            }
        }
        if(val!=1){
            ver[i].push_back(val);
            mp[make_pair(A[i],val)]=ver[i].size();
        }
       // for(auto v:ver[i])cout<<v<<" ";puts("");
    }
    dfs(1,-1);
    cout<<ans<<endl;
}

F

题意

m m 辆车,起点、终点、耗油量、最多加油次数。
n n 个地点,位置。
求最小油箱使得车都能在限制内到达。

题解

显然求出每辆车最多耗油即可,对于每辆车最多耗油去掉也就是最多路径长。
如果能加油 x x 次,就是把总路程切成 x + 1 x+1 段,里面最长的那段。
然后再取最大值即是答案。
d p [ i ] [ j ] [ k ] dp[i][j][k] 表示 i i j j 切成 k k 段中的最长段长度。
d p [ i ] [ j ] [ k ] = m i n ( m a x ( d p [ i ] [ p o s ] [ k 1 ] , p [ j ] p [ p o s ] ) ) dp[i][j][k]=min(max(dp[i][pos][k-1],p[j]-p[pos]))
容易发现,我们必须去掉一个 m a x max .
前者随着 p o s pos 变大,后者则变小。初始前者是 0 0
所以 m a x max 一开始是后者,然后是前者。
也就是只有两个决策点。
并且随着 j j 增大,前者和后者最多同时加上新的一段,前者可能会加的更小。
所以能保证,到达决策点后, j + + j++ 之后,决策点也在往前。

这是可以单调的。


#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define sf(x) scanf("%d",&x)
using namespace std;
typedef long long ll;
const int maxn = 250050;
 
int n,m;
int dp[403][403][403],p[401];
int s[maxn],t[maxn],c[maxn],r[maxn];
 
int main(){
    cin>>n>>m;
    FOR(i,1,n)sf(p[i]);
    FOR(i,1,m){
        sf(s[i]),sf(t[i]);
        sf(c[i]),sf(r[i]);
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            dp[i][j][1]=p[j]-p[i];
        }
    }
    for(int k=2;k<=n+1;k++){
        for(int i=1;i<=n;i++){
            int pos=i;
            for(int j=i+1;j<=n;j++){
                while(pos<=j&&dp[i][pos][k-1]<p[j]-p[pos])pos++;
                dp[i][j][k]=min(p[j]-p[pos-1],dp[i][pos][k-1]);
            }
        }
    }
    ll ans=0,tmp;
    for(int i=1;i<=m;i++){
        tmp=1ll*c[i]*dp[s[i]][t[i]][r[i]+1];
        ans=max(ans,tmp);
    }
    cout<<ans<<endl;
}
发布了203 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/mxYlulu/article/details/104117505