D1T1.Vigenère密码
模拟
#include<iostream> #include<cstdio> using namespace std; int main() { string a,b;cin>>b>>a; for(int i=0;i<a.size();i++) { bool ok=0;if(a[i]>='a'&&a[i]<='z')a[i]=a[i]-'a'+'A',ok=1;int j=i%b.size(); if(b[j]>='a'&&b[j]<='z')b[j]=b[j]-'a'+'A'; int c=((a[i]-'A')-(b[j]-'A')+26)%26;if(ok==0)cout<<(char)(c+'A');else cout<<(char)(c+'a'); } return 0; }
D1T2.国王游戏
贪心+高精度
我们知道,一个排序是经过好多次交换变成的,我们分析一下何时两个大臣需要交换,
设第一个大臣左右手数字为(a1,b1),第二个大臣左手数字为(a2,b2),第一个大臣前面的所有大臣和国王左手数字的乘积为x
那么交换前第一个大臣的奖赏为x/b1,第二个为(x*a1)/b2
交换后第一个大臣为x/b2,第二个为(x*a2)/b1
为了好看,我们设c1=x/b1,c2=(x*a1)/b2,c3=x/b2,c4=(x*a2)/b1
因为a>=1,b>=1,所以c4>=c1,c2>=c3
比较c2与c4的大小,可以发现,当a1/b2<a2/b1时,即a1*b1<a2*b2时,不交换是更优的,所以我们应该让a*b小的排在前面
记得写高精度!!!!!!!!!!!!!!!!!
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct HP{ int s,n[10001]; }aa[1001]; struct xxx{ int a,b; }x[1001]; bool cmp(xxx a,xxx b) { return a.a*a.b<b.a*b.b; } void Max(HP a,HP b,HP &c) { memset(c.n,0,sizeof(c.n)); if(a.s>b.s) c=a; if(a.s<b.s) c=b; if(a.s==b.s) { for(int i=a.s;i>=1;i--) { if(a.n[i]>b.n[i]){c=a;break;} if(a.n[i]<b.n[i]){c=b;break;} } if(c.s==0)c=a; } } void cheng(HP a,HP b,HP &c) { memset(c.n,0,sizeof(c.n)); for(int i=1;i<=a.s;i++) for(int j=1;j<=b.s;j++) { c.n[i+j-1]+=(a.n[i]*b.n[j])%10000; c.n[i+j]+=(a.n[i]*b.n[j])/10000; } if(c.n[a.s+b.s])c.s=a.s+b.s; else c.s=a.s+b.s-1; } void chu(HP a,int b,HP &c) { memset(c.n,0,sizeof(c.n)); int now=0; for(int i=a.s;i>=1;i--) { now+=a.n[i];c.n[i]=now/b;now=now%b*10000; } if(c.n[a.s])c.s=a.s;else c.s=a.s-1; } int main() { int n;scanf("%d",&n);for(int i=0;i<=n;i++)scanf("%d%d",&x[i].a,&x[i].b); sort(x+1,x+n+1,cmp); for(int i=0;i<=n;i++)aa[i].n[1]=x[i].a,aa[i].s=1; HP MAX,tot=aa[0];chu(tot,x[1].b,MAX);cheng(tot,aa[1],tot); for(int i=2;i<=n;i++) { HP v;chu(tot,x[i].b,v);Max(v,MAX,MAX);cheng(tot,aa[i],tot); } int d[5],dd=0; while(MAX.n[MAX.s]) { d[++dd]=MAX.n[MAX.s]%10;MAX.n[MAX.s]/=10; } for(;dd;dd--)printf("%d",d[dd]); for(int i=MAX.s-1;i>=1;i--)printf("%04d",MAX.n[i]); return 0; }
D1T3.开车旅行
双向链表+倍增
先用双向链表求出每个点的小A和小B能到达的点,具体做法如下:
先排序,然后按数据在原数组的位置遍历,寻找每个数左边,左边的左边,右边,右边的右边的最小值和次小值
找完一个数,删一个数。把每个点小A开一次,小B开一次,算作一次
fa[i][j]表示从i点开2^j次到达的点,aa[i][j]表示从i点开2^j次小A开的距离,bb[i][j]表示从i点开2^j次小B开的距离
fa[i][j]=fa[fa[i][j-1]][j-1];
aa[i][j]=aa[i][j-1]+aa[fa[i][j-1]][j-1];
bb[i][j]=bb[i][j-1]+bb[fa[i][j-1]][j-1];
为了避免比值是double,所以a:b<c:d当ad<cb时,成立(c必须不等于0)
最后,如果ansb一直是0的话,应输出海拔最高的
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; int a[100011],b[100011],n,m,x[10011],s[10011],bh[100011]; int fa[100011][20];long long aa[100011][20],bb[100011][20]; struct xxx{ int h,lnext,rnext,no; }data[100011]; bool cmp(xxx a,xxx b){return a.h<b.h;} void gx(int _h,int _no,int h,int i,int &Min) { if(_h<=Min) { if(_h==Min)a[i]=_no; else {a[i]=b[i];Min=_h;b[i]=_no;} } else if(abs(h-data[bh[a[i]]].h)>_h)a[i]=_no; } void ab() { data[0].lnext=0;data[0].rnext=0;data[0].h=2000000003; for(int i=1;i<=n;i++)data[i].lnext=i-1,data[i].rnext=i+1; data[n].rnext=0; for(int i=1;i<=n;i++) { int Min=1000000001; int no=data[bh[i]].no,h=data[bh[i]].h,l=data[bh[i]].lnext,r=data[bh[i]].rnext; int l2h,l1h,r1h,r2h,l2no,l1no,r1no,r2no,l2,l1,r1,r2; l1=data[l].lnext;l2=data[l1].lnext; r1=data[r].rnext;r2=data[r1].rnext; l1h=data[l].h;l2h=data[l1].h; r1h=data[r].h;r2h=data[r1].h; l1no=data[l].no;l2no=data[l1].no; r1no=data[r].no;r2no=data[r1].no; if(l1)gx(h-l2h,l2no,h,i,Min); if(l)gx(h-l1h,l1no,h,i,Min); if(r)gx(r1h-h,r1no,h,i,Min); if(r1)gx(r2h-h,r2no,h,i,Min); if(l)data[l].rnext=r;if(r)data[r].lnext=l; } } void st() { for(int i=1;i<=n;i++) { if(a[i])aa[i][0]=abs(data[bh[i]].h-data[bh[a[i]]].h); if(b[a[i]])bb[i][0]=abs(data[bh[a[i]]].h-data[bh[b[a[i]]]].h); fa[i][0]=b[a[i]]; } for(int j=1;j<=16;j++) for(int i=1;i<=n;i++) { fa[i][j]=fa[fa[i][j-1]][j-1]; aa[i][j]=aa[i][j-1]+aa[fa[i][j-1]][j-1]; bb[i][j]=bb[i][j-1]+bb[fa[i][j-1]][j-1]; } } void Ans(int u,int x,long long &ansa,long long &ansb) { for(int i=16;i>=0;i--) { if(fa[u][i]&&aa[u][i]+bb[u][i]<=x) { ansa+=aa[u][i],ansb+=bb[u][i]; x=x-aa[u][i]-bb[u][i];u=fa[u][i]; } } if(aa[u][0]<=x)ansa+=aa[u][0]; } int main() { scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&data[i].h);data[i].no=i;} sort(data+1,data+n+1,cmp);for(int i=1;i<=n;i++)bh[data[i].no]=i; ab();st();int x; scanf("%d%d",&x,&m); long long ans=data[n].no,ansa=0ll,ansb=0ll;Ans(data[n].no,x,ansa,ansb); for(int i=n-1;i>=1;i--) { int u=data[i].no; long long aa=0ll,bb=0ll;Ans(u,x,aa,bb); if(ansa==0||aa*ansb<bb*ansa)ansa=aa,ansb=bb,ans=u; } if(ansb==0)ans=data[n].no; printf("%lld\n",ans); for(int i=1;i<=m;i++) { int s,x;scanf("%d%d",&s,&x); long long aa=0ll,bb=0ll;Ans(s,x,aa,bb); printf("%lld %lld\n",aa,bb); } return 0; }
D2T1.同余方程
ax ≡ 1 (mod b)意味着ax-by==1,求x
exgcd,ax-by==1可以看成ax+b(-y)==1
#include<iostream> #include<cmath> using namespace std; int x,y; void gcd(int a,int b) { if(b==0){x=1;y=0;} else { gcd(b,a%b); int t=x;x=y;y=t-a/b*y; } } int main() { int a,b; cin>>a>>b; gcd(a,b); x=(x%b+b)%b; cout<<x; return 0; }
D2T2.借教室
二分+差分
由于剩余的教室是非递增的,所以我们可以二分答案
差分数组cf[]来差分r[]数组,cf[i]=r[i]-r[i-1]
每次租借s到t的d间教室,只要cf[s]-=d;cf[t+1]+=d;即可
然后还原原数组,如果剩余教室个数<0,就借不到这份订单,反之可以
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int cf[1000002],n,m,d[1000001],s[1000001],t[1000001],r[1000001]; bool check(int ans) { for(int i=1;i<=n+1;i++) { cf[i]=r[i]-r[i-1]; } for(int i=1;i<=ans;i++) { cf[s[i]]-=d[i];cf[t[i]+1]+=d[i]; } int last=0; for(int i=n;i>=1;i--) { int x=last-cf[i+1];last=x;if(x<0)return false; } return true; } int main() { scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&r[i]); for(int i=1;i<=m;i++)scanf("%d%d%d",&d[i],&s[i],&t[i]); int l=1,r=m+1,ans=0; while(l<r){ int mid=(l+r)/2; if(check(mid))l=mid+1; else ans=mid,r=mid; } if(ans==0)cout<<0; else cout<<-1<<endl<<ans; return 0; }