2017 10 03 NOIP2017模拟赛

考崩了。。。没有一道AC。。


写书 2979

只有10分。。。
有一个10敲成9了 对拍两个小时 。。。
这种题目下次对拍造数据可以不随机 直接从1循环到n


LR棋盘 2980

鼓捣一个多小时 想用组合数写 最后只能把LR切开打暴力 只有40
考完发现其实是DP
其实跟上次欢乐赛的第二题挺像的 上次秒A,这次居然没看出来。。。

由题意知道 棋子之间的相对位置是不变的
那么就可以DP了
定义dp[i]为以第i个棋子结尾的方案数
预处理每个棋子能到达的位置
再枚举棋盘上的位置 循环棋子 并判断能否到达 更新dp数组

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;

#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define INF 0x3f3f3f3f
#define LL long long
#define M 100005
#define P 1000000007
#define T 6000007

char str[M];
struct node{
    int L,R;
}A[M];
LL dp[M];
int n,m;

void Insert(int s){
    if(str[s]=='.') return ;
    ++m;
    if(str[s]=='L')A[m].L=1,A[m].R=s;
    if(str[s]=='R')A[m].L=s,A[m].R=n;
}
int main() {
    scanf("%s",str+1);
    n=strlen(str+1);
    FOR(i,1,n) Insert(i);
    dp[0]=1;
    FOR(i,1,n)DOR(j,m,1)
        if(i>=A[j].L&&i<=A[j].R)
            dp[j]=(dp[j]+dp[j-1])%P;
    printf("%lld\n",dp[m]);
    return 0;
}

道路评价 2981

当时就直接n*n打了个暴力 有60分 反而这题得分最高。。。
想到枚举每条边 判两边数量 再更新ans 但如果直接暴力求数量 还是要n*n的复杂度
主要是并查集还不怎么6

正解就是通过并查集来压缩路径
当然直接循环的话 就可能把没用到的边给压没掉
可以分开求maxl和minl

首先每个集合数量为1 也就是它本身
当求某一条两边的合法数量时
如果某一个集合的最优解并不如当前线段
那么这个集合就可以直接并入整个集合

如果可以保证当前边始终由于当前树中的连边 就可以直接压缩已经更新的路径了
那么sort一下就好了

如果求minl就将所有边按权值从大到小排序 就可以保证最优了
且当前不存在的连边就不需要继续向下找

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;

#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define INF 0x3f3f3f3f
#define LL long long
#define M 100005

inline void chkmi(int &a,int b){if(a>b)a=b;}
inline void chkmx(int &a,int b){if(a<b)a=b;}
inline int MIN(int a,int b){if(a<b)return a;return b;}
inline int MAX(int a,int b){if(a>b)return a;return b;}

struct node{int to,v;};
struct edge{
    int fr,to,v;
    bool operator <(const edge &a)const{
        return v>a.v;   
    }
}A[M];
int mark[M],Fa[M],cnt[M];
int n;
LL maxl,minl;

int Find(int x){return Fa[x]==x?x:Fa[x]=Find(Fa[x]);}
void Init(){FOR(i,1,n) Fa[i]=i,cnt[i]=1;}

int main(){
    cin>>n;
    FOR(i,2,n)scanf("%d%d%d",&A[i].fr,&A[i].to,&A[i].v);
    sort(A+1,A+n+1);
    Init();
    FOR(i,1,n-1){
        int a=Find(A[i].fr);
        int b=Find(A[i].to);
        Fa[a]=b;
        minl+=1ll*A[i].v*cnt[a]*cnt[b];
        cnt[b]+=cnt[a];
    }
    Init();
    DOR(i,n-1,1){
        int a=Find(A[i].fr);
        int b=Find(A[i].to);
        Fa[a]=b;
        maxl+=1ll*A[i].v*cnt[a]*cnt[b];
        cnt[b]+=cnt[a];
    }
    cout<<maxl-minl<<endl;
    return 0;   
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值