• 暑期集训第十五天(7-6)题解及总结


    小总结:

    今天上午又轮到老姚出题了,老姚这次出题莫名多了一道数论(说好的不出数论呢???),还有就是一如既往的dp多,四道题里面有两道dp题目......

    明天高三学长要高考了,祝愿他们能取得的一个好成绩吧(稍微带一下我呗~)

    T1:精灵魔法

    看到这一道题乍一看是没有什么思路的,但是详细一想,我们可以对编号进行离散化,记录一下一个人前面还有多少人编号比他大,之后以速度为基准进行排序,再数出一个点前面有多少人编号比他大,减一下就好了,原因是两个点的互换在一个点超过一个点和一个点被超过是等价的,我们只统计其中一种就可以了,这里统计的是一个点超过的点的个数,于是我们考虑从大到小枚举,把前面的点记录一下他比几个点大,之后轮到某个点之后看一下前面有几个点比他大,这样就可以统计答案了,这是什么,是区间修改,单点查询,于是选择用线段树进行维护,注意在对速度进行排序的时候要特别处理一下相等的情况,不然会100->70.

    其实关于这道题的主流做法是单点修改,区间查询(和我正好相反),还可以用归并排序求逆序对,我都试着打了一下,这里就不放代码了,退一下老师的博客:https://www.cnblogs.com/hbhszxyb/p/13253975.html

     1 #include<bits/stdc++.h>
     2 #define int long long
     3 #define debug printf("-debug-
    ")
     4 #define lson (t<<1)
     5 #define rson (t<<1|1)
     6 #define mid ((l+r)>>1)
     7 using namespace std;
     8 const int N=1e6+10;
     9 struct Tree{
    10     int w,lazy,siz;
    11 }tree[N];
    12 void pushup(int t){
    13     tree[t].w=tree[lson].w+tree[rson].w;
    14 }
    15 void build(int t,int l,int r){
    16     tree[t].siz=r-l+1;
    17     if(l==r) return;
    18     build(lson,l,mid);build(rson,mid+1,r);
    19     pushup(t);
    20 }
    21 void pushdown(int t){
    22     tree[lson].w+=tree[t].lazy;
    23     tree[lson].lazy+=tree[t].lazy;
    24     tree[rson].w+=tree[t].lazy;
    25     tree[rson].lazy+=tree[t].lazy;
    26     tree[t].lazy=0;
    27 }
    28 void change(int t,int l,int r,int cl,int cr){
    29     if(cl<=l&&r<=cr){
    30         tree[t].w+=tree[t].siz;
    31         tree[t].lazy++;
    32         return;
    33     }
    34     pushdown(t);
    35     if(cr<=mid) change(lson,l,mid,cl,cr);
    36     else if(cl>mid) change(rson,mid+1,r,cl,cr);
    37     else{
    38         change(lson,l,mid,cl,cr);change(rson,mid+1,r,cl,cr);
    39     }
    40     pushup(t);
    41 }
    42 int query(int t,int l,int r,int pos){
    43     if(l==r){
    44         return tree[t].w;
    45     }
    46     pushdown(t);
    47     if(pos<=mid) return query(lson,l,mid,pos);
    48     else return query(rson,mid+1,r,pos);
    49 }
    50 int ans=0,cnt=0;
    51 struct Node{
    52     int num,v;
    53 }a[N];
    54 bool cmp1(Node a,Node b){
    55     return a.num<b.num;
    56 }
    57 bool cmp2(Node a,Node b){
    58     if(a.v==b.v) return a.num<b.num;
    59     return a.v<b.v;
    60 }
    61 signed main(){
    62     int n;
    63     scanf("%lld",&n);
    64     build(1,1,n);
    65     for(int i=1;i<=n;++i){
    66         scanf("%lld",&a[i].num);
    67     }
    68     for(int i=1;i<=n;++i) scanf("%lld",&a[i].v);
    69     sort(a+1,a+n+1,cmp1);
    70     for(int i=1;i<=n;++i) a[i].num=++cnt;//离散化
    71     sort(a+1,a+n+1,cmp2);
    72     for(int i=n;i>=1;--i){
    73         int cnt1=n-a[i].num;
    74         int cnt2=query(1,1,n,a[i].num);
    75         if(cnt1-cnt2>0) ans+=cnt1-cnt2;
    76         if(a[i].num>1) change(1,1,n,1,a[i].num-1);
    77     }
    78     printf("%lld
    ",ans);
    79     return 0;
    80 }
    View Code

    T2:最小环

     这道题的题目是误导人的......我们从数据范围之中可以得到n^3的最小环求发是一定不行的,并且你求出的最小环不一定包含一号节点

    这道题目考虑我们如果想要用最短路来跑,要避免的情况就是从1节点出去的方向再次回到1号节点,于是我们可以考虑把从1号节点出去的方向的路线断掉,之后再从被隔离的节点跑一遍最短路,对1号节点此时的距离进行取最小值就可以了。

    这到题奇葩的dijksta会T掉,Spfa反而可以,但是我考试的时候吸氧水过了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=1e6+10;
     4 struct Node{
     5     int next,to,dis;
     6 }edge[N];
     7 int Head[N],tot=1;
     8 void Add(int x,int y,int z){
     9     edge[++tot].to=y;
    10     edge[tot].dis=z;
    11     edge[tot].next=Head[x];
    12     Head[x]=tot;
    13 }
    14 struct Edge{
    15     int num,dis;
    16     Edge(int x,int y){
    17         num=x;dis=y;
    18     }
    19     bool operator < (const Edge &a)const{
    20         return a.dis<dis;
    21     }
    22 };
    23 priority_queue<Edge>q;
    24 int dis[N],vis[N],ans=0x3f3f3f3f;
    25 void dijkstra(int x){
    26     memset(dis,0x3f,sizeof(dis));
    27     memset(vis,0,sizeof(vis));
    28     dis[x]=0;q.push(Edge(x,0));
    29     while(!q.empty()){
    30         int u=q.top().num;q.pop();
    31         if(vis[u]) continue;
    32         vis[u]=1;
    33         for(int i=Head[u];i;i=edge[i].next){
    34             int v=edge[i].to;
    35             if(dis[v]>dis[u]+edge[i].dis){
    36                 dis[v]=dis[u]+edge[i].dis;
    37                 q.push(Edge(v,dis[v]));
    38             }
    39         }
    40     }
    41 }
    42 void Init(){
    43     memset(edge,0,sizeof(edge));
    44     memset(Head,0,sizeof(Head));
    45     tot=1;ans=0x3f3f3f3f;
    46 }
    47 void Solve(){
    48     Init();
    49     int n,m;
    50     scanf("%d%d",&n,&m);
    51     for(int i=1;i<=m;++i){
    52         int x,y,z;
    53         scanf("%d%d%d",&x,&y,&z);
    54         Add(x,y,z);Add(y,x,z);
    55     }
    56     for(int i=Head[1];i;i=edge[i].next){
    57         int v=edge[i].to;
    58         int cnt=edge[i].dis;
    59         //printf("%d %d %d
    ",v,edge[i].dis,edge[i^1].dis);
    60         edge[i^1].dis=0x3f3f3f3f;
    61         dijkstra(v);
    62         ans=min(ans,dis[1]+edge[i].dis);
    63         edge[i^1].dis=cnt;
    64     }
    65     if(ans!=0x3f3f3f3f) printf("%d
    ",ans);
    66     else printf("-1
    ");
    67 }
    68 int main(){
    69     int T;
    70     scanf("%d",&T);
    71     while(T--) Solve();
    72     return 0;
    73 }
    View Code

    T3:LGTB 与序列

     

     这道题一看是数论当时直接就放弃去看第四道题了,其实这道题和数论的关系并没有那么大,主要还是一道dp题目。

    我们可以考虑ai<=30,其实如果我们去的数字大于58了,就不如直接放1,反正1和任何数字互质,放多少个1都可以,另外,在2~58之内一共有16个质数,于是我们可以吧1~58里面的所有数字进行质因子分解,之后我们可以借助状压dp的思路来记录那些质因子已经被选过了,对于输入的数字我们进行降序排序,用16个质因子里面组成尽可能大的数字来应对a数组之中大的数字,之后就是一道很普通的状压dp题目了,和排列prem就没有什么不同了.

     

     1 #include <bits/stdc++.h>
     2 const int maxn=105,maxp=17,Inf=0x3f3f3f3f;
     3 int prime[] = {0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; 
     4 int n,a[maxn];
     5 int dp[maxp][1<<maxp];
     6 int _prime[maxn];
     7 bool cmp(int a,int b){
     8     return a>b;
     9 }
    10 int main(){
    11     scanf("%d",&n);
    12     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    13     std::sort(a+1,a+n+1,cmp);
    14     memset(_prime,0,sizeof(_prime));
    15     for(int i=1;i<=58;i++)
    16         for(int j=1;j<=16;j++)
    17             if(i<prime[j]) break;
    18             else if(i%prime[j]==0)
    19                 _prime[i]|=(1<<j-1);
    20     memset(dp,0x3f,sizeof(dp));
    21     dp[0][0]=0;
    22     for(int i=1;i<=std::min(n,16);i++)
    23         for(int j=0;j<(1<<16);j++) 
    24             for(int k=1;k<=58;k++)
    25                 if(!(_prime[k]&j)){
    26                     int s=j|_prime[k];
    27                     dp[i][s]=std::min(dp[i][s],dp[i-1][j]+abs(a[i]-k));
    28                 }
    29     int ans=Inf;
    30     for(int j=0;j<(1<<16);j++)
    31         ans=std::min(ans,dp[std::min(n,16)][j]);
    32     if(n>16)
    33         for(int i=17;i<=n;i++)
    34             ans+=abs(a[i]-1);
    35     printf("%d
    ",ans);
    36     return 0;
    37 }
    View Code

     

    T4:步步为零

     这道题一看就能看出是一道dp的题目,因为它的状态和转移十分明显,但是着手去写的时候你会发现总体思路越写越乱,于是最后我只打了一个爆搜水了20pts......

    这道题的dp数组与众不同的是它的数组是bool型的,dp[i][j][k]表示从起点走到(i,j)时得到的数字能否组成k这个数字

    到时间了....明天要高考,我们要提前回去,未完待续中........

     

  • 相关阅读:
    Error: listen EADDRINUSE: address already in use :::80报错解决
    kettle连接oracle
    Oracle 11g RAC使用Manual和Policy Managed方法配置
    Veritas NetBackup命令行添加ESXi主机,即时恢复虚拟机操作方法。
    在 ESXi 主机上关闭无响应的虚拟机电源 (1004340)
    [bug]记一次同步数据问题排查
    关于cmake和开源项目发布的那些事(PF)
    React 配置 @ 路径别名
    react httpproxymiddleware 跨域处理
    reactrouterconfig (嵌套路由)使用
  • 原文地址:https://www.cnblogs.com/li-jia-hao/p/13257534.html
Copyright © 2020-2023  润新知