7.4集训模拟赛7(白丢了100分的一天,WTF)
A. 侦查(tarjan缩点)
题目描述
输入格式
输出格式
样例
样例输入
12 11 10 11 2 3 4 5 1 1 1 1 1 1 1 2 2 3 1 3 4 5 5 6 6 7 8 9 9 12 11 12 10 11 8 10
样例输出
8 9 10 11 12 1 2 3
分析
我恨死这道题了,(so easy,but......),一个break写道括号外面了,结果第二问没有输出,~~~~~~~,
这就是个简单tarjan缩点,然后记录SCC中的基地数和敌人数然后取max,在遍历一边每个点找出输出就ok,break,不要写错位置啊啊啊啊啊啊啊啊!!!
Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1100; int n,m; int a[N]; int sum[N],summ[N],dfn[N],low[N]; int Time,tot,cnt,top; int x,y,g[N],head[N]; bool vis[N]; int sta[N]; struct edge{ int to; int ne; }e[N<<2]; void add(int u,int v){ e[++cnt].to = v; //e[cnt].w = w; e[cnt].ne = head[u]; head[u] = cnt; } void tarjan(int u){ dfn[u]=low[u]=++Time; vis[u]=1;sta[++top]=u; for(int i=head[u];i;i=e[i].ne){ int v = e[i].to; if(!dfn[v]){ tarjan(v); low[u] = min(low[u] , low[v]); } if(vis[v]){ low[u] = min(low[u] , dfn[v]); } } if(dfn[u]==low[u]){ tot++; //sum[tot]+=a[u]; while(sta[top+1]!=u){ int v = sta[top--]; vis[v] = 0; sum[tot]+=a[v]; summ[tot]++; g[v] = tot; } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(int i=1;i<=n;i++){ if(!dfn[i])tarjan(i); } int jidi=-10,diren=-10; for(int i=1;i<=tot;i++){ jidi = max(jidi,summ[i]); diren = max(diren,sum[i]); } //printf("%d %d\n",jidi,diren); for(int i=1;i<=n;i++){ if(summ[g[i]]==jidi){ for(int j=1;j<=n;j++){ if(g[j]==g[i]){ printf("%d ",j); } } break; } } printf("\n"); for(int i=1;i<=n;i++){ if(sum[g[i]]==diren){ for(int j=1;j<=n;j++){ if(g[j]==g[i]){ printf("%d ",j); } } break;//此处为窝的墓地 } } return 0; }
B. 借书(二分答案)
题目描述
输入格式
输出格式
样例
样例输入
6 3 5 7 1 17 13 10
样例输出
7
数据范围与提示
分析
就是道二分答案,其实这就是个题目模型,最小值最大化,最大值最小化(已经暗示是二分答案了),
Code
#include<bits/stdc++.h> using namespace std; const int N = 1e5+10; int n,m; int a[N]; int maxn; int gd[N];//差分数组,记录差值 int aa; int cnt; bool cheak(int x){ aa=0,cnt=0; for(int i=1;i<n;i++){ aa+=gd[i]; if(aa>=x){//如果大于就加一,并把aa置为零 cnt++;//方便下次在找 aa=0; } } if(cnt+1>=m){//如果大于m,及能够选出m本书 return true;//就true } else return false;//否则 } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); maxn = max(maxn,a[i]); } sort(a+1,a+1+n);//排序 for(int i=1;i<n;i++){ gd[i] = a[i+1]-a[i];//差分数组 } int l=0,r=maxn; while(l<=r){//二分 if(r-l==1){//相差为一,就判断一下,否则就会死死死死循环 if(cheak(r)){ l=r; } break; } int mid = (l+r)/2; if(cheak(mid)){ l=mid;//如果能cheak,说明还有更大的向上更新 } else {//不能就得向下更新 r=mid; } } printf("%d",l); }
C. 搜城探宝(树...树形dp?)
题目描述
输入格式
输出格式
样例
样例输入
8 4 1 2 1 3 2 4 2 5 3 6 3 7 6 8 2 5 1 4 6 1 1 10
样例输出
27
数据范围与提示
分析
额......这道题就是树形dp,不过要考虑传送门的问题,咋办昵!!!啧啧啧啧!!!!我们建一个虚点,就是n+1,因为情况不确定,所以可以枚举每一个点,让每一个点都做一次需点n+1的右子树,然后就dfs使劲搜。xiu,不过要注意一个点当完虚点后要归位。
Code
#include<bits/stdc++.h> using namespace std; const int N = 50; int ls[N],rs[N],fa[N];//左右儿子,以及父亲数组 int n,key,a[N]; int x,y,ans; int dfs(int x,int k){ if(x==0)return 0;//叶子节点返回0 if(k==1)return a[x];//只有一把钥匙开当前门 int now = 0; for(int i=0;i<k;i++){//钥匙从一枚举,以为可能都用于开左子树,右子树 now = max(dfs(ls[x],i)+dfs(rs[x],k-i-1)+a[x],now); } return now; } int main(){ scanf("%d%d",&n,&key); for(int i=1;i<=n-1;i++){ scanf("%d%d",&x,&y); if(!ls[x])ls[x] = y;//如果左儿子为空 else rs[x] = y;//否则为右儿子 fa[y]=x;//父亲 } for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } ls[n+1] = 1;//虚点左儿子为根节点 for(int i=2;i<=n;i++){ rs[n+1] = i; if(ls[fa[i]]==i){//若是左儿子 ls[fa[i]]=0; ans = max(ans,dfs(n+1,key+2)); ls[fa[i]] = i;//归位 } else {//是右儿子 rs[fa[i]] = 0; ans = max(ans,dfs(n+1,key+2)); rs[fa[i]] = i;归位 } } printf("%d\n",ans); return 0; }
D. MM不哭(dp)
题目描述
输入格式
输出格式
样例
样例输入
4 3 2 2 5 8 6 1 8 7
样例输出
56
分析
看到这想到了......
这和洛谷关路灯差不多,我们定义数组f[ i ][ j ][ 1(0) ]是处理完i到j最后停在i(0)或停到j(1)的最少消耗,那么
Code
#include<bits/stdc++.h> using namespace std; const int N = 1010; int n,v,x; int f[N][N][2]; int sum[N]; struct node{ int x; int w; }e[N]; bool cmp(node a,node b){//结构体排序 return a.x<b.x; } int main(){ scanf("%d%d",&n,&v); for(int i=1;i<=n;i++){ scanf("%d%d",&e[i].x,&e[i].w); } /*v = e[v].x;//这里加不加都一样 //printf("%d",x);//因为题中的数据就是递增的 sort(e+1,e+1+n,cmp);//虽然说没有说 for(int i=1;i<=n;i++){//不要问我怎么知道的 if(v==e[i].x){//问就是歪打正着 v=i; } }*/ for(int i=1;i<=n;i++){ sum[i] = sum[i-1]+e[i].w;//前缀和 } memset(f,0x3f,sizeof(f));//初始化最大值 f[v][v][1]=f[v][v][0] = 0;//在v位置没动时消耗为0 for(int l = 2;l <= n;l++){ for(int i=1;i+l-1<=n;i++){ int j = i+l-1; if(i < x-l+1)continue;//i不能在范围之外 //if(j>n)break; int n1=sum[i]+sum[n]-sum[j]; int n2=sum[i]+sum[n]-sum[j]; int n3=sum[i-1]+sum[n]-sum[j-1]; int n4=sum[i-1]+sum[n]-sum[j-1]; f[i][j][0] = min(f[i+1][j][0]+n1*(e[i+1].x-e[i].x),f[i+1][j][1]+n2*(e[j].x-e[i].x));//两个(额,或者说四个)状态 f[i][j][1] = min(f[i][j-1][0]+n3*(e[j].x-e[i].x),f[i][j-1][1]+n4*(e[j].x-e[j-1].x)); } } printf("%d\n",min(f[1][n][0],f[1][n][1]));//取两种情况的最小值 return 0; }
恭喜你找到一只正在洗澡的pig,稍等一会,分析马上来
由于这是记录日常博客,可能题分析的不是太多(主要是时间不够),那就贴上教练博客
也算偏偏访量吧