暑期集训7/20
题目连接:https://vjudge.net/contest/172124#overview
A题:
题目要求:火车按顺序1-n进站,让你判断出站顺序是否正确
思路:stack
代码:
#include <stdio.h> #include <iostream> #include <algorithm> #include <stack> #define rep(i,a,b) for(int i=a;i<b;++i) using namespace std; stack<int> st; int a[1005]; int main() { int n; while(~scanf("%d",&n)) { if(n==0) break; while(1) { int flag=1; scanf("%d",&a[1]); if(a[1]==0) break; st.push(1); rep(i,2,n+1) scanf("%d",a+i); int u=2,v=1; while(1) { if(a[v]==st.top()) { st.pop(); if(v==n) break; v++; if(!st.size()) { if(u==n+1) { flag=0;break; } st.push(u++); } }else { if(u==n+1) { flag=0;break; } st.push(u++); } } if(flag) cout<<"Yes"<<endl; else cout<<"No"<<endl; } cout<<endl; } return 0; }
B题:
题目要求:判断每个括号是不是都要匹配
思路:stack
代码:
#include<iostream> #include<string> #include<stack> using namespace std; int main() { int n; cin>>n; cin.get(); while(n--) { stack<char> s; string str; int flag=0; getline(cin,str); for(int i=0;i<str.size();i++) { if(str[i]=='['||str[i]=='(') s.push(str[i]); //如果是[ 或者 ( 就把它推进st里 else if(!s.empty()&&s.top()=='('&&str[i]==')') s.pop(); 如果st不为空,且能匹配的话 else if(!s.empty()&&s.top()=='['&&str[i]==']') s.pop(); else flag=1;如果st为空,这时候来了一个s[i],这样就永远匹配不了了 } if(!flag&&!s.size()) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; }
C题:
题目要求:就是求逆序数;
那什么是逆序数? 比如3 2 1,1和2就是逆序数
题目要求统计,逆序数变回正序要移动多少位置
那就是归并排序,归并排序的思想,把一一个数组分开成2份,一直重复,知道剩下的数列很小,用正常比较就可以很快的出来排序,然后在一步步并回去,求出最后的结果
代码:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <stack> #define rep(i,a,b) for(int i=a;i<b;i++) #define ll long long using namespace std; int a[500005]; int temp[500005]; ll ans; void bihe(int fi,int mid,int en) { int i=fi,j=mid+1,k=fi; while(i<=mid&&j<=en)//合并 { if(a[i]>a[j]) { ans+=j-k; temp[k++]=a[j++]; } else { temp[k++]=a[i++]; } } while(i<=mid) temp[k++]=a[i++];//如何前半段还有剩余就说明这些是剩下的(也就是其它的大的元素)我们就把它并到后面 while(j<=en) temp[k++]=a[j++];//如何后半段还有剩余就说明这些是剩下的(也就是其它的大的元素)我们就把它并到后面 rep(i,fi,en+1) a[i]=temp[i]; } void bifen(int fi,int en) { if(fi<en) { int mid = (fi+en)/2; bifen(fi,mid);把前半个分 bifen(mid+1,en);把后半个分 bihe(fi,mid,en); } } int main() { int n; while(~scanf("%d",&n)) { if(n==0) break; ans=0; rep(i,1,n+1) scanf("%d",a+i); bifen(1,n);//把1-n分开 cout<<ans<<endl; } return 0; }
D题:
E题:
F题:
题目要求:求一个人到他的所有亲戚家的最近距离
思路:数据很小,直接暴力
代码:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <cmath> #define rep(i,a,b) for(int i=a;i<(b);++i) #define ll long long #define inf 0x3f3f3f3f using namespace std; int r[505]; int num[30005]; int main() { int T; scanf("%d",&T); while(T--) { memset(num,0,sizeof(num)); int n; scanf("%d",&n); rep(i,0,n) scanf("%d",r+i),num[r[i]]++;//num[r[i]]表示这个街道的亲戚数量 int ans=inf; rep(i,0,n) { int sum=0; rep(j,0,n) { if(i==j) continue; sum+=num[r[i]]*abs(r[j]-r[i]); } //cout<<sum<<endl; ans=min(ans,sum); } cout<<ans<<endl; } return 0; }
G题:
sstream的使用,还有set_....的应用
H题:
题目要求:求组合成的合唱团(9个人,3个小队)的分数最高,要求每个小队的队员都不同
思路:暴力
代码:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #define rep(i,a,b) for(int i=a;i<(b);++i) #define ll long long using namespace std; int n; struct node { int x,y,z,v; }ans[105]; int g[10]; bool judge(int i,int j,int k) { memset(g,0,sizeof(g)); g[ans[i].x]++; g[ans[i].y]++; g[ans[i].z]++; g[ans[j].x]++; g[ans[j].y]++; g[ans[j].z]++; g[ans[k].x]++; g[ans[k].y]++; g[ans[k].z]++; rep(i,1,10) if(g[i]>1) return false; return true; } bool cmp(node a,node b) { return a.v>b.v; } int main() { int cnt=1; while(~scanf("%d",&n)) { if(n==0) break; rep(i,0,n) scanf("%d %d %d %d",&ans[i].x,&ans[i].y,&ans[i].z,&ans[i].v); int mx=-1; sort(ans,ans+n,cmp); int flag=0; rep(i,0,n) { rep(j,i+1,n) { rep(k,j+1,n) { if(judge(i,j,k)) { mx=max(mx,ans[i].v+ans[j].v+ans[k].v); } } } } printf("Case %d: ",cnt++); cout<<mx<<endl; } return 0; }
I题:
J题:
题目:求学生信仰的宗教的种类
思路:并查集
代码:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <stack> #define rep(i,a,b) for(int i=a;i<b;i++) #define ll unsigned long long using namespace std; stack<char> st; int f[50005]; int n; ll m; int ans[50005]; int fd(int a) { while(f[a]!=a) a=f[a]; return a; } void combine(int a,int b) { int na=fd(a),nb=fd(b); if(na!=nb) f[na]=nb; } void init() { rep(i,1,n+1) f[i]=i; } int main() { /*ll tt=50000*50000/2; cout<<tt;*/ int cnt=1; while(cin>>n>>m) { if(n==0&&m==0) break; ll num=0; memset(ans,0,sizeof(ans)); init(); int a,b; rep(i,0,m) { scanf("%d %d",&a,&b); combine(a,b); } rep(i,1,n+1) { int x=fd(i); if(!ans[x]) { num++; ans[x]=1; } } printf("Case %d: ",cnt++); cout<<num<<endl; } return 0; }
K题:
思路:并查集
代码:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <stack> #define rep(i,a,b) for(int i=a;i<b;i++) using namespace std; stack<char> st; int f[30005]; int n,m; int ans[30005]; int fd(int a) { while(f[a]!=a) a=f[a]; return a; } void combine(int a,int b) { int na=fd(a),nb=fd(b); f[na]=nb; } void init() { rep(i,1,n+1) f[i]=i; } int main() { int T; scanf("%d",&T); while(T--) { memset(ans,0,sizeof(ans)); scanf("%d %d",&n,&m); init(); int a,b; rep(i,0,m) { scanf("%d %d",&a,&b); combine(a,b); } rep(i,1,n+1) { int x=fd(i); ans[x]++; } sort(ans+1,ans+n+1); cout<<ans[n]<<endl; } return 0; }
L题:
设置虚拟节点,然后这样进行2操作的时候,可以防止移动一整颗树的情况
代码:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <queue> #include <stack> #include <map> #include <string> #define de(x) cout<<#x<<"="<<x<<endl; #define rep(i,a,b) for(int i=a;i<(b);++i) #define repd(i,a,b) for(int i=a;i>=(b);--i) #define ll long long #define mt(a,b) memset(a,b,sizeof(a)) const int maxn = 100010; using namespace std; int n,m; int fa[maxn*2], num[maxn*2], sum[maxn*2]; void init() { int j=n+1; rep(i,0,n+1) fa[i]=j,fa[j]=j,num[j]=1,sum[j++]=i;//每个节点新弄一个虚拟父亲,虚拟父亲保存这子节点的信息 } int fd(int x) { while(x!=fa[x]) x=fa[x]; return x; } void combine(int a,int b) { int na=fd(a),nb=fd(b); if(na==nb) return; fa[na]=nb; sum[nb]+=sum[na]; num[nb]+=num[na]; } void frto(int a,int b) { int na=fd(a),nb=fd(b); if(na==nb) return; fa[a]=nb; //这边不是na,因为我们的虚拟节点是用来保存信息的(一个以它为根的集合的数据) num[nb]++;num[na]--; sum[nb]+=a;sum[na]-=a; } int main() { while(~scanf("%d %d",&n,&m)) { init(); int cas; rep(i,0,m) { scanf("%d",&cas); int a,b; if(cas==1) { scanf("%d %d",&a,&b); combine(a,b); } if(cas==2) { scanf("%d %d",&a,&b); frto(a,b); } if(cas==3) { scanf("%d",&a); int na=fd(a); printf("%d %d\n",num[na],sum[na]); } } } return 0; }
M题;
题目:让你求连通1-n的最短路中的最长的一条子路
思路:K。。。。。。最短路,把边排序,然后一直从最短的边合并
代码:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #define rep(i,a,b) for(int i=a;i<(b);++i) #define ll long long using namespace std; int f[505]; int n; struct node { int x,y,len; }road[20005]; bool cmp(node a,node b) { return a.len<b.len; } int fd(int x) { while(x!=f[x]) x=f[x]; return x; } void init() { rep(i,1,n+1) f[i]=i; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); int l; init(); int cnt=0; rep(i,1,n+1) rep(j,1,n+1) { scanf("%d",&l); if(i==j||j<i) continue; road[cnt].x=i;road[cnt].y=j;road[cnt++].len=l; } sort(road,road+cnt,cmp); int ans=0; rep(i,0,cnt) { int fr=road[i].x,to=road[i].y,len=road[i].len; /*cout << " a: "; rep(j,1,n+1) cout << f[j]<<" "; cout << endl;*/ if(fd(fr)!=fd(to)) { f[fd(fr)]=fd(to); ans=max(ans,len); } } cout<<ans<<endl; } return 0; }
N题:
题目:
思路:优先队列
代码:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include<functional>//greater这个东西的头文件 #include <queue> #include <stack> #define rep(i,a,b) for(int i=a;i<b;i++) #define ll long long using namespace std; int main() { int n; while(~scanf("%d",&n)) { int ans=0; int a; priority_queue<int,vector<int>,greater<int> > q; //优先队列从小到大排 rep(i,0,n) { scanf("%d",&a); q.push(a); } int x,y; while(!q.empty()) { x=q.top(); //cout<<x<<endl; q.pop(); int flag=0; if(!q.empty()) { y=q.top(); q.pop(); x+=y; //cout<<x<<endl; flag=1; } if(flag) q.push(x),ans+=x;; } cout<<ans<<endl; } return 0; }
O题:
线段树
代码:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <stack> #include <iostream> #define rep(i,a,b) for(int i=a;i<b;i++) #define ll long long using namespace std; struct node { int x,y,sum; }tree[50005*4];//要4*节点的个数 int a[50005]; void buildtree(int fi,int en,int k) //创建线段树 { tree[k].x=fi;//k表示第几个线段树,每个线段树的孩子都是2*k和2*k+1 tree[k].y=en; if(fi==en) { tree[k].sum=a[fi];//如果l==r那他就是叶子节点,就直接付给他对应数组数值 //cout<<a[fi]<<endl; return; } int mid=(fi+en)/2; buildtree(fi,mid,k<<1);//递归创建子节点的 buildtree(mid+1,en,(k<<1)+1); tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum;//更新每个区间的和 } void add(int x,int v,int k)//更新单个节点的值,然后更新线段树 { if(tree[k].x==x&&tree[k].y==x) { tree[k].sum+=v; return; } int mid=(tree[k].x+tree[k].y)/2; if(x<=mid) add(x,v,k<<1); else add(x,v,(k<<1)+1); tree[k].sum = tree[k<<1].sum + tree[(k<<1)+1].sum; } void sub(int x,int v,int k) { if(tree[k].x==x&&tree[k].y==x) { tree[k].sum-=v; return; } int mid=(tree[k].x+tree[k].y)/2; if(x<=mid) sub(x,v,k<<1); else sub(x,v,(k<<1)+1); tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum; } int query(int l,int r,int k)//求区间的和 { //cout<<l<<" "<<r<<" "<<" "<<tree[k].x<<" "<<tree[k].y<<endl; if(tree[k].x>=l&&tree[k].y<=r) return tree[k].sum; int mid=(tree[k].x+tree[k].y)/2; int res=0; if(r<=mid) res+=query(l,r,k<<1); else if(l>mid) res+=query(l,r,(k<<1)+1); else { res+=query(l,mid,k<<1); res+=query(mid+1,r,(k<<1)+1); } return res; //return 1; } int main() { int T; scanf("%d",&T); int cnt=1; while(T--) { int n; scanf("%d",&n); rep(i,1,n+1) scanf("%d",a+i); buildtree(1,n,1); char s[5]; int t1,t2; printf("Case %d:\n",cnt++); while(scanf("%s",s)) { if(s[0]=='E') break; if(s[0]=='A') { int x,v; scanf("%d %d",&t1,&t2); add(t1,t2,1); }else if(s[0]=='Q') { scanf("%d %d",&t1,&t2); int tans=query(t1,t2,1); cout<<tans<<endl; }else if(s[0]=='S') { scanf("%d %d",&t1,&t2); sub(t1,t2,1); } } } return 0; }
P题:
线段树,这把有个难点,就是区间的更新,我们用了延迟标记,代码里有详解
代码:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <queue> #include <stack> #include <map> #include <string> #define de(x) cout<<#x<<"="<<x<<endl; #define rep(i,a,b) for(int i=a;i<(b);++i) #define repd(i,a,b) for(int i=a;i>=(b);--i) #define ll long long #define mt(a,b) memset(a,b,sizeof(a)) const int maxn = 100010; using namespace std; struct node { int x,y; ll sum; }tree[maxn*4]; ll a[maxn]; ll ad[maxn*4]; void pushdown(int k) { int l=tree[k].x,r=tree[k].y; int mid=(l+r)/2; ad[k<<1]+=ad[k]; ad[(k<<1)+1]+=ad[k]; tree[(k<<1)+1].sum+=(r-mid)*ad[k]; tree[(k<<1)].sum+=(mid-l+1)*ad[k]; ad[k]=0; } void buildtree(int l,int r,int k) { tree[k].x=l; tree[k].y=r; ad[k]=0; if(l==r) { tree[k].sum=a[l]; return; } int mid=(l+r)/2; buildtree(l,mid,k<<1); buildtree(mid+1,r,(k<<1)+1); tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum; } void add(int l,int r,int v,int k) { int L=tree[k].x,R=tree[k].y; if(L>r||R<l) return ; if(l<=L&&r>=R)//就是到这里就停了,不再更新子节点了,然后我们标记一下这个点的子节点还没更新 ,就是ad{k】这个数组来标记 { ad[k]+=v; tree[k].sum+=(R-L+1)*v; return; } if(ad[k]) pushdown(k);//当我们要的区间是这个节点下面的时候,我们就要判断这个节点下面是不是要更新 int mid=(L+R)/2; if(r<=mid) add(l,r,v,k<<1); else if(l>mid) add(l,r,v,(k<<1)+1); else { add(l,mid,v,k<<1); add(mid+1,r,v,(k<<1)+1); } tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum; } ll query(int l,int r,int k) { int L=tree[k].x,R=tree[k].y; if(l<=L&&r>=R) { return tree[k].sum; } if(ad[k]) pushdown(k);//当我们要的区间是这个节点下面的时候,我们就要判断这个节点下面是不是要更新 int mid=(L+R)/2; ll res=0; if(r<=mid) res+=query(l,r,k<<1); else if(l>mid) res+=query(l,r,(k<<1)+1); else { res+=query(l,mid,k<<1); res+=query(mid+1,r,(k<<1)+1); } return res; } int main() { int n,m; while(~scanf("%d %d",&n,&m)) { rep(i,1,n+1) scanf("%lld",a+i); buildtree(1,n,1); char ch; int l,r,v; rep(i,0,m) { cin>>ch; if(ch=='Q') { scanf("%d %d",&l,&r); ll tans=query(l,r,1); cout<<tans<<endl; } if(ch=='C') { scanf("%d %d %d",&l,&r,&v); add(l,r,v,1); } } } return 0; }