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

会有一股神秘感。 提交于 2020-01-31 00:42:11

D

题意

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

题解

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

#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

题意

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

题解

显然求出每辆车最多耗油即可,对于每辆车最多耗油去掉也就是最多路径长。
如果能加油xx次,就是把总路程切成x+1x+1段,里面最长的那段。
然后再取最大值即是答案。
dp[i][j][k]dp[i][j][k]表示iijj切成kk段中的最长段长度。
dp[i][j][k]=min(max(dp[i][pos][k1],p[j]p[pos]))dp[i][j][k]=min(max(dp[i][pos][k-1],p[j]-p[pos]))
容易发现,我们必须去掉一个maxmax.
前者随着pospos变大,后者则变小。初始前者是00
所以maxmax一开始是后者,然后是前者。
也就是只有两个决策点。
并且随着jj增大,前者和后者最多同时加上新的一段,前者可能会加的更小。
所以能保证,到达决策点后,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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!