暑期集训第十五天(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-\n")
 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\n",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\n",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\n",ans);
66     else printf("-1\n");
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\n",ans);
36     return 0;
37 }
View Code

 

T4:步步为零

 

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

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

 

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

 

 

posted @ 2020-07-06 21:12  19502-李嘉豪  阅读(171)  评论(1编辑  收藏  举报