A.题意:通过加奇数减偶数的操作从a到b最少需要几步
签到题
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <deque> #include <map> using namespace std; typedef long long ll; const double inf=1e20; const int maxn=100005; int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++){ ll a,b; scanf("%lld%lld",&a,&b); ll c=b-a; if(c==0){ printf("0\n"); }else if(c>0&&c%2==0){ printf("2\n"); }else if(c<0&&c%2!=0){ printf("2\n"); } else printf("1\n"); } return 0; }
B.题意:有一个数组p,你可以任意次交换a[pi]和a[pi+1],问能不能把数组变成一个非严格上升的子序列。
解:很明显,如果p数组中有连续的一段,那么这一段数字就是可以任意交换的,把连续的若干段都进行排序,就已经尽最大可能满足题目要求了,在与满足条件的数组比较一下即可。
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <deque> #include <map> using namespace std; typedef long long ll; const double inf=1e20; const int maxn=200+10; void merge_sort(int *a,int x,int y,int *t){ if(y-x>1){ int m=x+(y-x)/2; int p=x,q=m,i=x; merge_sort(a,x,m,t); merge_sort(a,m,y,t); while(p<m||q<y){ if(q>=y||(p<m&&a[p]<=a[q]))t[i++]=a[p++]; else t[i++]=a[q++]; } for(i=x;i<y;i++)a[i]=t[i]; } } int a[maxn],b[maxn],c[maxn],d[maxn],e[maxn]; int main(){ int t; scanf("%d",&t); while(t--){ int n,m; scanf("%d%d",&n,&m); for(int i=0;i<n;i++){ scanf("%d",&a[i]); d[i]=a[i]; } for(int i=0;i<=n;i++)c[i]=0; for(int i=0;i<m;i++){ scanf("%d",&b[i]); b[i]--; c[b[i]]=1; } c[n]=0; int l,r; l=r=-1; for(int i=0;i<=n;i++){ if(c[i]==1){ if(l==-1){ l=i; } }else{ if(l!=-1){ merge_sort(a,l,i+1,e); l=-1; } } } sort(d,d+n); int o=1; for(int i=0;i<n;i++){ if(a[i]!=d[i])o=0; } if(o)printf("YES\n"); else printf("NO\n"); } return 0; }
C.题意:求每个字母出现的次数
解:因为错误的顺序并不会影响结果,所以直接先处理短的,再处理长的。
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <deque> #include <map> using namespace std; typedef long long ll; const double inf=1e20; const int maxn=2*1e5+10; int a[maxn]; char s[maxn]; int w[26],e[26]; int main(){ int t; scanf("%d",&t); while(t--){ int n,m; scanf("%d%d",&n,&m); getchar(); scanf("%s",s); for(int i=0;i<m;i++){ scanf("%d",&a[i]); } sort(a,a+m); for(int i=0;i<26;i++){ w[i]=e[i]=0; } int j=0; for(int i=0;i<n;i++){ w[s[i]-'a']++; while(a[j]-1==i&&j<m){ j++; for(int k=0;k<26;k++){ e[k]+=w[k]; } } } for(int k=0;k<26;k++){ e[k]+=w[k]; } for(int i=0;i<26;i++){ if(i<25)printf("%d ",e[i]); else printf("%d\n",e[i]); } } return 0; }
D.题意:给a,b,c。每一步你可以让abc中任意一个加1或者减1.求让c是b的倍数且b是a的倍数最少需要多少步?
解:首先考虑这样一个问题,题目其他条件不变,只把c去掉。那么问题就变成了给a,b。每一步你可以让ab中任意一个加1或者减1.求让b是a的倍数最少需要多少步?
两个做法:1,很明显,如果a确定了,b也就是确定的。那么就可以通过枚举a来确定b。时间复杂度为枚举a的时间复杂度。2,如果b确定,那么a就是b的因子中最接近原来的a的那个,说明a也是确定的。时间复杂度为枚举b的时间复杂度*求b因子的时间复杂度。
回到原题:做法就比较显而易见了,两个做法:1.显然b是最关键的一个字。所以枚举b,a用b的因子确定,c可以直接确定。时间复杂度为枚举b的时间复杂度*求b因子的时间复杂度。2.枚举a,这时因为有c的存在,显然不能直接确定b是多少,所以枚举b为a的倍数,对于每个b都可以确定唯一一个c。时间复杂度为枚举a*枚举a的倍数O(nlogn)
下代码为做法2,需要注意的是枚举b时需要比1e4大
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <deque> #include <map> using namespace std; typedef long long ll; const double inf=1e20; const int maxn=2*1e5+10; int main(){ int t; scanf("%d",&t); while(t--){ int m=1e5; int a,b,c; int a1,b1,c1; scanf("%d%d%d",&a,&b,&c); for(int i=1;i<=1e4;i++){ int num=0; for(int j=i;j<=2e4;j+=i){ num=0; num+=max(i-a,a-i); num+=max(j-b,b-j); int k; if(c%j>j/2){ k=(c+j-1)/j*j; }else{ k=c/j*j; } if(k<j)k=j; num+=max(k-c,c-k); if(num<m){ m=num; a1=i; b1=j; c1=k; } } } printf("%d\n",m); printf("%d %d %d\n",a1,b1,c1); } return 0; }
E.题意,一个n节点的二叉树,能不能让所有节点的深度和为m,如果能,构造出这棵树。
解:1.很明显,如果一颗n节点树是一条链,那么此时的深度和为n节点树所能达到的上线。
2.同理,如果一颗n节点的二叉树为完全二叉树,那么此时的深度和为n节点树所能达到的下线。
3.如何证明下线到上线之间每个数字都是可取的呢,假设现在又n个节点形成一条链,深度和为m,如何构造一颗深度和为m-1的树,答:最深 的那个节点移动到上一层。如何构造一颗深度和为m-2的树?答:还是刚才那个节点再向上移动一层。如此反复,直到此节点不能向上移动,那么开始移动下个一最深的节点。每一步都使得深度和减一,并且由一条链开始,最所有节点无法移动时会变成一颗完全二叉树。
4.如何模拟这个过程构造树。如果直接模拟这个过程是非常难模拟的,这道题的关键字是深度,所以我再这个模拟过程中去掉其他东西,只保留每个深度有多少节点,最后问题就变成了已知每个深度有多少节点,构造一颗二叉树。
5.补充:代码中的b数组代表第i层最多容纳b[i]个节点,那么有一行为if(b[i]>10000)b[i]=10000;时什么意思,答,因为是二叉树,所以b每次*2,有5000个节点,也就是最深为5000层,早就爆int了,但同时发现,只有5000个节点,也就是说在这个做法中最多的时候一层只有2500个。所以当b>2500的时候,需要赋值为一个大于2500的数字就能保证准确性。
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <deque> #include <map> using namespace std; typedef long long ll; const double inf=1e20; const int maxn=2*1e5+10; int a[maxn]; int b[maxn]; int c[maxn]; int main(){ int t; scanf("%d",&t); while(t--){ ll n,d; ll s,x; scanf("%lld%lld",&n,&d); s=(0+n-1)*n/2; x=0; ll nn=n; for(int i=1,j=0;nn>0;i*=2,j++){ if(nn>=i){ x+=i*j; }else{ x+=nn*j; } nn-=i; } //printf("%lld %lld \n",x,s); if(x<=d&&d<=s){ printf("YES\n"); for(int i=0;i<n;i++){ a[i]=1; if(i==0)b[i]=1; else b[i]=b[i-1]*2; if(b[i]>10000)b[i]=10000; } int j=n-1; int jj=n-1; int qwe=s-d; for(int i=0;i<qwe;i++){ //printf("%d %d \n",a[j-1],b[j-1]); if(a[j-1]>=b[j-1]){ j=jj; //printf("....."); } if(j==jj&&a[jj]==1){ jj--; } a[j]--; a[--j]++; /* for(int k=0;k<=jj;k++){ printf("%d ",a[k]); }printf("\n"); */ } int k=1; int kk=1; for(int i=1;i<=jj;i++){ for(int j=0;j<a[i];j++){ k++; c[k]=kk; if(j%2==1)kk--; } kk=k; } for(int i=2;i<=n;i++){ printf("%d ",c[i]); } printf("\n"); }else printf("NO\n"); } return 0; }
F.
解:1.首先是最明显的一个条件,两个点要么会相遇,要么不会相遇,且不会相遇的两点在时间为0时最近。
2.最简单的思路为枚举i枚举j时间复杂度为n2但显然超时,那么就只能线性枚举,当我们枚举到第i个的时候,前面的点有的在我们左边,有的在我们右边,有的往左走,有的往右走,有的比i点快,有的比i点满。何解?
2.1首先发现,枚举i枚举j和ij的顺序无关,所以我们可以给数组按位置排序,排序后枚举到第i个点时,前面的点都在i的左侧。
2.2说起运动,就有相对运动和绝对运动,题目中描述了一种相对坐标系的运动,如果给所有点都加上一个+1e9的速度,明显所有点的相对位置时不会变的,但是都变成了向右移动。这时发现相遇是否之和速度大小有关系,和值是多少关系不大,所以明显可以对速度离散化。
2.3假设遍历到第i个点位置为x,速度为v,我们要得到是前i-1个点中不能与之相遇的点与i点的距离差的和。那么就需要知道这个样的点有多少个,假设为y,以及这些点到坐标原点0之间的距离和sum。那么前i-1个点中不能与之相遇的点与i点的距离差的和=x*y-sum
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <deque> #include <map> using namespace std; typedef long long ll; const double inf=1e20; const int maxn=2e5+10; #define ls l,m,rt<<1; #define rs m+1,,r,rt>>1|1; ll n; struct aa{ ll a,v; aa(){} aa(ll a_,ll v_):a(a_),v(v_){} }a[maxn]; bool cmp(aa a,aa b){ return a.a<b.a; } ll m; ll t[maxn]; void Init_hash(){ for(int i=1;i<=n;i++){ t[i]=a[i-1].v; } sort(t+1,t+1+n); m=unique(t+1,t+1+n)-t-1; } int hashh(int x){ return lower_bound(t+1,t+1+m,x)-t; } ll sum[maxn<<2]; void pushup(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(ll l,ll r,ll rt){ if(l==r){ sum[rt]=0; return ; } ll m=(l+r)/2; build(l,m,rt<<1); build(m+1,r,rt<<1|1); pushup(rt); } void update(ll L,ll C,ll l,ll r,ll rt){ if(l==r){ sum[rt]+=C; return; } ll m=(l+r)>>1; if(L<=m)update(L,C,l,m,rt<<1); else update(L,C,m+1,r,rt<<1|1); pushup(rt); } ll query(ll L,ll R,ll l,ll r,ll rt){ if(L<=l&&r<=R)return sum[rt]; ll m=(l+r)/2; ll ans=0; if(L<=m)ans+=query(L,R,l,m,rt<<1); if(R> m)ans+=query(L,R,m+1,r,rt<<1|1); return ans; } ll sum2[maxn<<2]; void pushup2(int rt){ sum2[rt]=sum2[rt<<1]+sum2[rt<<1|1]; } void build2(ll l,ll r,ll rt){ if(l==r){ sum2[rt]=0; return ; } ll m=(l+r)/2; build2(l,m,rt<<1); build2(m+1,r,rt<<1|1); pushup2(rt); } void update2(ll L,ll C,ll l,ll r,ll rt){ if(l==r){ sum2[rt]+=C; return; } ll m=(l+r)>>1; if(L<=m)update2(L,C,l,m,rt<<1); else update2(L,C,m+1,r,rt<<1|1); pushup2(rt); } ll query2(ll L,ll R,ll l,ll r,ll rt){ if(L<=l&&r<=R)return sum2[rt]; ll m=(l+r)/2; ll ans=0; if(L<=m)ans+=query2(L,R,l,m,rt<<1); if(R> m)ans+=query2(L,R,m+1,r,rt<<1|1); return ans; } int main(){ scanf("%lld",&n); for(int i=0;i<n;i++){ scanf("%lld",&a[i].a); } for(int i=0;i<n;i++){ scanf("%lld",&a[i].v); } Init_hash(); sort(a,a+n,cmp); build(1,n,1); build2(1,n,1); ll num=0; for(int i=0;i<n;i++){ ll vv=hashh(a[i].v); ll x=query(1,vv,1,n,1); ll summ=query2(1,vv,1,n,1); num+=(x*a[i].a-summ); //printf("%lld %lld...\n",x,summ); update(vv,1,1,n,1); update2(vv,a[i].a,1,n,1); //printf("%lld\n",num); } printf("%lld\n",num); return 0; }
来源:https://www.cnblogs.com/wz-archer/p/12364827.html