水
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=10010; const int inf=0x3f3f3f3f; int n,m; ll l1,r1,l2,r2; int main(){ cin>>n; while(n--) { cin>>l1>>r1>>l2>>r2; if(l1!=l2) cout<<l1<<" "<<l2<<endl; else cout<<l1<<" "<<r2<<endl; } }
题意:给出x,y两个数的所有因子,如果一个因子同时存在于xy中,那么这个因子在数列中会出现两次,让你还原xy。
首先最大的那个数肯定是x,然后统计每个数字出现次数,稍微想想就好了。
水。
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=10010; const int inf=0x3f3f3f3f; int n,m; ll a[maxn]; int vis[10010]; int main(){ cin>>n; ll maxx=0; for(int i=1;i<=n;i++) { cin>>a[i]; maxx=max(a[i],maxx); vis[a[i]]++; } printf("%lld ",maxx); ll x=1; for(int i=1;i<=10000;i++) { if(maxx%i==0){ vis[i]--; } } for(int i=1;i<=10000;i++) { if(vis[i]) maxx=i; } printf("%lld ",maxx); }
题意:给出一个灯的排列,使同样的灯间隔为3,并且rgb三个颜色要循环,求改变灯的最少次数。
rgb总共有六种全排列,全列出来,暴力比较。
比赛时看错题目 wa两发 代码还写的丑 我是弟弟。
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=2*100010; const int inf=0x3f3f3f3f; int n,m; char s[maxn]; string op="RGB"; int main(){ cin>>n; cin>>s+1; int ans=inf; int tep=0; int f=0; op="RGB"; for(int i=1;i<=n;i++) { if(s[i]!=op[i%3]){ tep++; } } if(tep<ans){ f=1; ans=tep; } op="BRG"; tep=0; for(int i=1;i<=n;i++) { if(s[i]!=op[i%3]){ tep++; } } if(tep<ans){ f=2; ans=tep; } op="GBR"; tep=0; for(int i=1;i<=n;i++) { if(s[i]!=op[i%3]){ tep++; } } if(tep<ans){ f=3; ans=tep; } op="RBG"; tep=0; for(int i=1;i<=n;i++) { if(s[i]!=op[i%3]){ tep++; } } if(tep<ans){ f=4; ans=tep; } op="BGR"; tep=0; for(int i=1;i<=n;i++) { if(s[i]!=op[i%3]){ tep++; } } if(tep<ans){ f=5; ans=tep; } op="GRB"; tep=0; for(int i=1;i<=n;i++) { if(s[i]!=op[i%3]){ tep++; } } if(tep<ans){ f=6; ans=tep; } printf("%d ",ans); if(f==2){ for(int i=1;i<=n;i++) { if(i%3==1)printf("R"); if(i%3==2)printf("G"); if(i%3==0)printf("B"); } } if(f==3){ for(int i=1;i<=n;i++) { if(i%3==1)printf("B"); if(i%3==2)printf("R"); if(i%3==0)printf("G"); } } if(f==4){ for(int i=1;i<=n;i++) { if(i%3==1)printf("B"); if(i%3==2)printf("G"); if(i%3==0)printf("R"); } } if(f==5){ for(int i=1;i<=n;i++) { if(i%3==1)printf("G"); if(i%3==2)printf("R"); if(i%3==0)printf("B"); } } if(f==6){ for(int i=1;i<=n;i++) { if(i%3==1)printf("R"); if(i%3==2)printf("B"); if(i%3==0)printf("G"); } } if(f==1){ for(int i=1;i<=n;i++) { if(i%3==1)printf("G"); if(i%3==2)printf("B"); if(i%3==0)printf("R"); } } puts(""); }
题意:还是给出一个灯的排列,要使相邻的灯颜色不一样,求改变灯的最少次数。
贪心的想,如果有两个相邻的灯,我肯定要改变其中一个,那么优先改变后面的肯定是最优的,因为我在改变第i+1个灯的时候,不但可以使其和 i 不一样,还能使其和i+2不一样。
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=2*100010; const int inf=0x3f3f3f3f; int n,m; char s[maxn]; char ans[maxn]; string op="RGB"; int main(){ cin>>n; cin>>s+1; s[n+1]='c'; int tot=0; ans[1]=s[1]; for(int i=2;i<=n;i++) { if(s[i]==ans[i-1]){ for(int j=0;j<3;j++) { if(op[j]!=ans[i-1]&&op[j]!=s[i+1]){ ans[i]=op[j]; break; } } tot++; }else{ ans[i]=s[i]; } } printf("%d ",tot); for(int i=1;i<=n;i++) { printf("%c",ans[i]); } puts(""); }
(E1) Array and Segments (Easy version)
题意:给出n个数字,这里n的范围是300,再给出m个区间,对于每个区间,你可以使下标在这个区间内所有数字都-1,也可以不操作。现在要求最优操作,使得操作过后最大值减最小值 最大,输出答案,并且输出选取了几个区间和哪些区间。
这道题和hard版本差的只是数据范围,有一个思想是通用的,就是假设 第 i 个数字是最后的最小值,那么使所有覆盖了这个 i 的所有区间都生效后,答案一定是 在选取了 i 后最优的。为什么呢?由于 i 是最小值,假设 j 是最大值,如果 j 没有被 i 连累 ,那么差值肯定会变大(i变小了,j没变),如果被 i 连累了,那么两个同时缩小,对答案没影响。所以我们只要枚举所有的 i 使 所有包含了 i 区间生效就可以了。
对于每一次枚举,在这个简单版本里,应该可以(没试过,时间复杂度是能结束的)暴力的把每一个区间都减去,然后比完最大值 - a[ i ]后,再把他还原。这样做的时间复杂度是o(n^3)。
我用了01差分。时间复杂度优化到o(n^2).
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=310; const int inf=0x3f3f3f3f; int n,m; int a[maxn],b[maxn],l[maxn],r[maxn]; int c[maxn]; int main(){ cin>>n>>m; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } for(int i=1;i<=m;i++) { scanf("%d%d",&l[i],&r[i]); } int ans=0,teo=1,tep=0; for(int i=1;i<=n;i++) { CLR(c,0); for(int j=1;j<=m;j++) { if(l[j]<=i&&r[j]>=i){ c[l[j]]--; c[r[j]+1]++; } } int maxx=-inf; for(int j=1;j<=n;j++) { c[j]+=c[j-1]; b[j]=a[j]+c[j]; maxx=max(b[j],maxx); } if(maxx-b[i]>ans) { ans=maxx-b[i]; teo=i; } } printf("%d ",ans); int tt[maxn]; for(int i=1;i<=m;i++) { if(l[i]<=teo&&r[i]>=teo){ tt[++tep]=i; } } printf("%d ",tep); for(int i=1;i<=tep;i++) { printf("%d",tt[i]); if(i<tep)printf(" "); else puts(""); } }
E2. Array and Segments (Hard version)
题意:和上题一样,n变成1e5.
上题的时间复杂度不能被接受了,所以我们要考虑优化。
看上一个版本,n的枚举显然是没法优化了,但是我们发现每次我们在对m个区间进行操作时浪费了很多的时间,所以我们在这里优化。
如果一个区间已经覆盖了[4,5],那么在我们处理完4这个点的信息后,显然不需要还原,因为这个区间对5还是有用的,如果我们把一个区间在 l 这个位置加入,在 r 这个位置消除,那我们对每一个区间就只需要处理两次,然后我们用线段树来维护a[ n ]数组的最大值,并且用线段树完成 -1 +1的操作,就可以了。
所以我们要先处理出这些区间是在哪个地方插入的,哪个地方删除的,所以还是用到差分的思想,每个 i 都开一个vector,然后对于标号为id 的区间 l r,往v[ l ]里塞入id, v[ r ]里塞入-id,正数代表我要加入某一个区间的影响,负数代表我要消除某一个区间的影响。
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; const int inf=0x3f3f3f3f; ll a[maxn]; struct node{ ll ma,lazy; }tr[maxn<<2]; vector<int > v[maxn]; int l[maxn],r[maxn],n,m,tt[310]; ll ans,teo,tep; void init(){ ans=0,teo=0,tep=0; for(int i=1;i<=n;i++) { v[i].clear(); } } void build(int o,int l,int r){ if(l==r){ tr[o].ma=a[l]; tr[o].lazy=0; return ; } int mid=(l+r)>>1; build(o<<1,l,mid); build((o<<1)|1,mid+1,r); tr[o].ma=max(tr[o<<1].ma,tr[(o<<1)|1].ma); tr[o].lazy=0; } void pushup(int o){ tr[o].ma=max(tr[o<<1].ma,tr[o<<1|1].ma); } void pushdown(int o,int l,int r){ if(tr[o].lazy){ tr[o<<1].lazy+=tr[o].lazy; tr[o<<1|1].lazy+=tr[o].lazy; tr[o<<1].ma+=tr[o].lazy; tr[o<<1|1].ma+=tr[o].lazy; tr[o].lazy=0; } } void update(int o,int l,int r,int ql,int qr,ll val){ if(ql<=l&&r<=qr){ tr[o].lazy+=val; tr[o].ma+=val; return; } pushdown(o,l,r); int mid=l+((r-l)>>1); if(ql<=mid)update(o<<1,l,mid,ql,qr,val); if(qr>=mid+1)update(o<<1|1,mid+1,r,ql,qr,val); pushup(o); } ll query(int o,int l,int r,int ql,int qr){ if(ql<=l&&qr>=r)return tr[o].ma; pushdown(o,l,r); int mid=(l+r)>>1; ll ans=-inf; if(ql<=mid)ans=query(o<<1,l,mid,ql,qr); if(qr>=mid+1)ans=max(ans,query(o<<1|1,mid+1,r,ql,qr)); return ans; } int main(){ while(cin>>n>>m) { init(); build(1,1,n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } build(1,1,n); for(int i=1;i<=m;i++) { scanf("%d %d",&l[i],&r[i]); v[l[i]].push_back(i); v[r[i]+1].push_back(-i); } for(int i=1;i<=n;i++) { int sz=v[i].size(); for(int j=0;j<sz;j++) { int x=v[i][j]; if(x>=0){ update(1,1,n,l[x],r[x],-1); }else{ x=-x; update(1,1,n,l[x],r[x],1); } } ll maxx=query(1,1,n,1,n); ll b=query(1,1,n,i,i); if(maxx-b>ans){ ans=maxx-b; teo=i; } } //printf("debug "); for(int i=1;i<=m;i++) { if(l[i]<=teo&&r[i]>=teo){ tt[++tep]=i; } } printf("%lld %lld ",ans,tep); for(int i=1;i<=tep;i++) { printf("%d%c",tt[i],(i<tep)?' ':' '); } } }
题意:给出一幅图,这幅图可能有很多最小生成树,现在你可以操作若干次,每次操作可以任选一条边使其权值+1,一条边可以多次选择,使得最小生成树唯一,并且最小生成树的权值不变。
首先克鲁斯卡尔跑一遍生成树,然后把有用边重新构图,变成一棵新的树,把所有无用边都标记一下,然后求边的两个端点和生成树形成的环的最大长度是不是等于自己,如果等于自己,则代表这条边可以取代这个最大长度,成为生成树的一部分。(环上不会有大于自己的边,否则就不会是最小生成树了)。求最大长度的方法是lca。
求lca的时候,dfs千万不要把1号节点的父节点设成-1,否则会re,oj可能还不报re只报wa,kk要是再这样写,kk就把题目吃下去。
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=2e5+10; struct edge{ int u,v; ll w; friend bool operator <(const edge &a,const edge &b){ return a.w<b.w; } }; struct node{ int to,Next; ll w; }te[maxn<<1]; vector<edge>e; int n,m,tot,dep[maxn]; int u,v,fa[maxn],head[maxn],f[maxn][30]; ll w,ma[maxn][30]; void addv(int u,int v,ll w){ te[++tot].to=v; te[tot].w=w; te[tot].Next=head[u],head[u]=tot; } bool vis[maxn]; void init(){ tot=0; CLR(head,-1); CLR(vis,0); e.clear(); for(int i=1;i<=n;i++)fa[i]=i; } int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); } void dfs(int u,int fa,int deep,ll w){ f[u][0]=fa,dep[u]=deep,ma[u][0]=w; for(int i=head[u];i != -1;i=te[i].Next){ int v=te[i].to; if(v==fa)continue; dfs(v,u,deep+1,te[i].w); } } void work(){ for(int j=1;j<=20;j++) { for(int i=1;i<=n;i++) { f[i][j]=f[f[i][j-1]][j-1]; ma[i][j]=max(ma[i][j-1],ma[f[i][j-1]][j-1]); } } } ll lca(int p,int q){ if(dep[p]<dep[q])swap(p,q); ll res=0; for(int i=20;i>=0;i--) { if(dep[f[p][i]]>=dep[q]) { res=max(res,ma[p][i]); p=f[p][i]; } } if(p==q)return res; for(int i=20;i>=0;i--) { if(f[p][i]!=f[q][i]){ res=max(res,ma[p][i]); res=max(res,ma[q][i]); p=f[p][i],q=f[q][i]; } } return max(res,max(ma[p][0],ma[q][0])); } int main(){ while(cin>>n>>m) { init(); for(int i=1;i<=m;i++) { scanf("%d%d%lld",&u,&v,&w); e.push_back({u,v,w}); } sort(e.begin(),e.end()); for(int i=0;i<m;i++) { u=e[i].u,v=e[i].v; w=e[i].w; int fx=find(u),fy=find(v); if(fx!=fy){ fa[fx]=fy; addv(u,v,w); addv(v,u,w); }else{ vis[i]=1; } } dfs(1,0,1,0); work(); int ans=0; for(int i=0;i<m;i++) { if(vis[i]){ if(e[i].w==lca(e[i].u,e[i].v)){ ans++; } } } printf("%d ",ans); } }