vjudge速度训练1

爆零祭.

A - Tree and Permutation (HDU 6446) 传送门

题目大意:给出N个点,每个点之间有一条权值为w的边,然后这些点有N!种排列,每种排列对应一种遍历点的路径 求这N!种路径的长度和。

每一条边 (u,v) 当成一个元素 ,与剩余 n-2 个元素组成全排列,总共有 (n-1)! 种情况,所以 (u,v) 总共出现了 (N-1)! 次,(v,u) 同理

所以只要求出所有边(单向)的和再乘 (n-1)!*2 就是答案

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int mod=1e9+7;
int n;
ll dp[N],sum[N],jc[N];
struct Node {ll w,s; };
vector<Node> tree[N];
void dfs(ll u,ll v) {
    sum[u]=1;
    for (int i=0;i<tree[u].size();i++) {
        ll son=tree[u][i].s,
            val=tree[u][i].w;
        if (son==v) continue;
        dfs(son,u);
        sum[u]+=sum[son];
        dp[u]=(dp[u]+dp[son]+((sum[son]*(n-sum[son]))%mod*val)%mod)%mod;
    }
}
int main() {
    jc[0]=1;
    for (int i=1;i<=N;i++) jc[i]=(jc[i-1]*i)%mod;
    while (~scanf("%d",&n)) {
        for (int i=0;i<=n;i++) tree[i].clear();
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        for (int i=1;i<n;i++) {
            ll x,y,z; Node t;
            scanf("%lld%lld%lld",&x,&y,&z);
            t.w=z,t.s=y; 
            tree[x].push_back(t);
            t.s=x; tree[y].push_back(t);
        }
        dfs(1,-1);
        ll ans=(2*(dp[1]*jc[n-1])%mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

B - Clarke and points (HDU 5626) 传送门

很水的一道题,考场上竟然没有想到…
把 |xA−xB|+|yA−yB| 的所有情况列出来然后找最大就行了
比如    |xA−xB|+|yA−yB|
     =xA-xB+yB-yA
     =(xA-yA)+(yB-xB)
扫一遍然后记录下最大的(xA-yA)和最大的(yB-xB)加起来 其他情况同理。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
const int INF=2e9+5;
ll n,seed,T,x[N],y[N];
ll x1,x2,x3,x4,yy1,y2,y3,y4;
inline ll rand(ll l, ll r) { 
  static ll mo=1e9+7, g=78125; 
  return l+((seed*=g)%=mo)%(r-l+1); 
}
int main() {
    scanf("%lld",&T);
    while (T--) {
        scanf("%lld%lld",&n,&seed);
        for (int i=1;i<=n;i++) {
            x[i]=rand(-1000000000, 1000000000);
            y[i]=rand(-1000000000, 1000000000);
        }
        x1=x2=x3=x4=yy1=y2=y3=y4=-INF;
        for (int i=1;i<=n;i++) {
            x1=max(x1,x[i]+y[i]); yy1=max(yy1,-x[i]-y[i]);
            x2=max(x2,y[i]-x[i]); y2=max(y2,x[i]-y[i]);
            x3=max(x3,-x[i]-y[i]); y3=max(y3,x[i]+y[i]);
            x4=max(x4,x[i]-y[i]); y4=max(y4,y[i]-x[i]);
        }
        ll ans=-INF;
        ans=max(ans,x1+yy1); ans=max(ans,x2+y2);
        ans=max(ans,x3+y3); ans=max(ans,x4+y4);
        printf("%lld\n",ans);
    }
    return 0;
}

竟然发现y1这个变量不能定义 和系统变量冲突 瑟瑟发抖…]

C - Buy and Resell (HDU 6438) 传送门

题目大意:给出 n ,表示 n 天。给出 n 个数,a[i] 表示第 i 天,物品的价格是多少。每天可以选择买一个物品,或者卖一个已有物品,也可以什么都不做,问最后最大能赚多少钱,最少操作次数是多少?

依次遍历每一天,比如当前是第 i 天,用堆维护前 i - 1 天的最小值,如果最小值比 a[i] 大,直接将 a[i] 加入堆里就可以了,如果日后遇到比 a[i] 大的再更新答案。如果最小值比 a[i] 小,那么就将其卖出,更新答案: 赚的钱 ans += a[i] - min, 操作次数 cnt += 2,但是这样并不能保证答案一定最优的,因为后面可能遇到收购价格更高的那一天,采取的方法是将 min 替换成 a[i],并记录 a[i] 是已经交换过的数,如果日后遇到比 a[i] 大的数 a[j],即遇到更优答案了,就再卖一次 a[i],更新答案,但是注意此时钱的更新是没有问题的,但是操作次数就不能累加了,这样相当于min买,a[j]卖。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,a[N],T; 
bool vis[N];
struct Cube {
    int d,x;
    bool operator < (const Cube &u) const{
        return u.x<x || u.x==x && vis[u.d]>vis[d];
    }
} tmp,now;
priority_queue<Cube> q;
int main() {
    scanf("%d",&T);
    while (T--) {
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        while (!q.empty()) q.pop();
        memset(vis,false,sizeof(vis));
        ll ans=0,num=0;
        tmp.x=a[1]; tmp.d=1; q.push(tmp);
        for (int i=2;i<=n;i++) {
            now=q.top();
            if (a[i]>now.x) {
                q.pop();
                ans+=a[i]-now.x;
                if (vis[now.d]) {
                    vis[now.d]=false;
                    q.push(now);
                    //如果此时堆顶元素now是已经交换过得,那么更新答案以后相当于now没有交换
                    //所以堆顶元素不用出堆,且要标记为false
                }
                else num+=2;
                vis[i]=true;
            }
            tmp.d=i; tmp.x=a[i]; q.push(tmp);
        }
        printf("%lld %lld\n",ans,num);
    }
    return 0;
}

D - YJJ’s Salesman (HDU 6447) 传送门

题目大意:1e5个点,问从(0,0)走到(1e9,1e9)的最大收益。
当你从(u-1,v-1)走到(u,v)时,你可以获得点(u,v)的权值。

将每个点按y轴从小到大排序,如果y轴相同则按x轴从大到小排序(x轴坐标要离散化),这样枚举的每一个点它左边的点都是更新过得,只要用树状数组维护 c[ x ] 表示 坐标 <= x的dp最大值,然后每个点用 c[ x-1 ] 更新就行了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
#define lowbit(x) (x&-x)
int T,n,m,c[N],s[N],dp[N];
struct Node {
    int x,y,v;
} a[N];
inline bool cmp (Node i,Node j) {
    return i.y<j.y || i.y==j.y && i.x>j.x;
}
int query(int x) {
    int res=0;
    while (x>0) {
        res=max(res,c[x]);
        x-=lowbit(x);
    }
    return res;
}
void update(int x,int v) {
    while (x<=n) {
        c[x]=max(c[x],v);
        x+=lowbit(x);
    }
}
int main() {
    scanf("%d",&T);
    while (T--) {
        scanf("%d",&n);
        memset(c,0,sizeof(c));
        for (int i=1;i<=n;i++) {
            scanf("%d%d%d",&s[i],&a[i].y,&a[i].v);
            a[i].x=s[i];
        }
        n++; s[n]=1e9; a[n].x=1e9; a[n].y=1e9; a[n].v=0;
        sort(s+1,s+1+n);
        for (int i=1;i<=n;i++) {
            a[i].x=lower_bound(s+1,s+1+n,a[i].x)-s;
        }
        sort(a+1,a+1+n,cmp);
        for (int i=1;i<=n;i++) {
            dp[i]=query(a[i].x-1)+a[i].v;
            update(a[i].x,dp[i]);
        }
        printf("%d\n",dp[n]);
    }
}

E - Neko’s loop (HDU 6444) 传送门

题意 : 给出一个 n 个元素的环、可以任意选择起点、选完起点后、可以行走 m 步、每次前进 k 个单位、所走到的点将产生正或负贡献、问你一开始得准备多少才能使得初始资金加上在环上获取最大利益不少于给定的 s

根据k我们会得到一个循环周期,很明显如果循环周期是负的那一定就不走一个循环了。那直接求最长不超过m的最大连续子段和就行了。
如果循环周期大于0,就有两种可能了(循环周期次数a=m/len(len为周期长度),b=m%n):
1.走完a圈后再加上不超过b的最大连续字段和
2.走完a-1圈后再加上不超过len的最大连续字段和,因为刚好走完一圈不一定会比走了一部分多,加上一圈反而会使最大值变少。

(网上代码,懒得写了) 主要是单调队列维护求长度不超过m的最大连续子段和 模板

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define lowbit(x) x&(-x)
const int mod=1e9+7;
const int mx=1e5+5;
typedef long long ll;
ll n,m,k,s,l,r;
bool vis[mx];
vector<ll>g[mx];
ll sum[mx],a[mx],q[mx];
void del(ll k,ll m){
    while(l<r&&k-q[l]>m)l++;
}
void ins(ll k){
    while(l<r&&sum[q[r-1]]>=sum[k]) r--;
    q[r++] = k;
}
ll getans(ll i,ll k){
    int n = g[i].size();
    for(int j = 1; j <= n; j++)
        sum[j] = g[i][j-1],sum[j] += sum[j-1];
    for(int i = 1; i <= n; i++)
        sum[i+n] = sum[n]+sum[i];
    for(int i = 1; i <= n; i++)
        sum[i+2*n] = sum[2*n]+sum[i];
    l = r = 0; ll ans = 0;
    ins(0);
    for(int i = 1; i <= 3*n; i++){
        del(i,k);
        if(ans<sum[i]-sum[q[l]])
            ans = sum[i]-sum[q[l]];
        ins(i);
    }
    return ans;
}
int main(){
    int t,ca = 1;
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld%lld%lld",&n,&s,&m,&k);
        for(int i = 0; i < n; i++)
            g[i].clear();
        for(int i = 0; i < n; i++)
            scanf("%lld",&a[i]);
        int cnt = 0;
        memset(vis,0,sizeof(vis));
        for(int i = 0; i < n; i++){
            if(!vis[i]){
                int j = i;
                while(!vis[j]){
                    vis[j] = 1;
                    g[cnt].push_back(a[j]);
                    j = (j+k)%n;
                }
                cnt++;
            }
        }
        ll ans = 0;
        for(int i = 0; i < cnt; i++){
            int len = g[i].size();
            ll tmp = 0;
            for(int j = 0; j < len; j++)
                tmp += g[i][j];
            ans = max(ans,getans(i,m));
            if(tmp<0)
                continue;
            ll op = m%len;
            ll d = m/len;
            tmp = max(d-1,0ll)*tmp;
            if(d>=1)
                op += len;
            if(op==0){
                ans = max(ans,tmp);
                continue;
            }
            ans = max(ans,tmp+getans(i,op));
        }
        printf("Case #%d: %lld\n",ca++,max(0ll,s-ans));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42149421/article/details/82594671