T1小凯的疑惑
小凯手中有两种面值的金币,两种面值均为正整数且彼此互素。每种金币小凯都有 无数个。在不找零的情况下,仅凭这两种金币,有些物品他是无法准确支付的。现在小 凯想知道在无法准确支付的物品中,最贵的价值是多少金币?注意:输入数据保证存在 小凯无法准确支付的商品。
Solution
2017数论题,感觉是历史以来最难的一道。
对于ax+by=c(a>=0,b>=0)当方程无解是,必定为x<0&&y>0且下一组解x>0&&y<0那x为-1,y为a-1,那c就是a*b-a-b。
Code
#include<iostream> #include<algorithm> using namespace std; long long a,b; int main() { cin>>a>>b; cout<<a*b-a-b; }
T2时间复杂度
题面太长不粘了
模拟题,注意细节。。
Code
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int t,vis[1009],tag,tag2,n,ans,num,tot,s[109],st[109],top,maxx=10000000,yy,zz; char c,cc[109],x,y[10],z[10]; int main() { cin>>t; while(t--) { memset(vis,0,sizeof(vis)); scanf("%d",&n); tag=tag2=tot=num=ans=top=0; cin>>cc; int p=strlen(cc); if(cc[2]=='n') for(int j=4;j<=p;++j) if(cc[j]>='0'&&cc[j]<='9') num=num*10+cc[j]-'0'; else break; for(int i=1;i<=n;++i) { cin>>c; if(c=='F') { cin>>x>>y>>z; yy=zz=0; if(vis[x])tag=1; vis[x]=1; s[++top]=x; if(y[0]!='n') { int o=strlen(y); for(int k=0;k<o;++k) yy=yy*10+y[k]-'0'; } else yy=maxx; if(z[0]!='n') { int o=strlen(z); for(int k=0;k<o;++k) zz=zz*10+z[k]-'0'; } else zz=maxx; if(yy<maxx&&zz==maxx) { st[top]=1; tot++; } else if(yy>zz) { st[top]=-1; tag2++; } else { st[top]=0; } if(!tag2)ans=max(ans,tot); } else { if(!top)tag=1; if(st[top]==1)tot--; else if(st[top]==-1)tag2--; vis[s[top]]=0; top--; } } if(top||tag) { cout<<"ERR "; continue; } if(ans==num)cout<<"Yes "; else cout<<"No "; } return 0; }
T3逛公园
T4奶酪
现有一块大奶酪,它的高度为 hh,它的长度和宽度我们可以认为是无限大的,奶酪 中间有许多 半径相同 的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中, 奶酪的下表面为z = 0z=0,奶酪的上表面为z = hz=h。
现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐 标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别 地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果 一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。
位于奶酪下表面的 Jerry 想知道,在 不破坏奶酪 的情况下,能否利用已有的空洞跑 到奶酪的上表面去?
#include<iostream> #include<cstdio> #include<cmath> #define N 1012 using namespace std; int n,t,f[N]; double h,r; struct ssd{ double x,y,z; }a[N]; double calc(int i,int j){ return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)+(a[i].z-a[j].z)*(a[i].z-a[j].z)); } int find(int x){return f[x]=f[x]==x?x:find(f[x]);} int main(){ scanf("%d",&t); while(t--){ scanf("%d%lf%lf",&n,&h,&r); for(int i=0;i<=n+1;++i)f[i]=i; for(int i=1;i<=n;++i){ scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].z); for(int j=1;j<i;++j) if(calc(i,j)<=r*2){ int xx=find(i),yy=find(j); if(xx!=yy)f[xx]=yy; } if(a[i].z-r<=0){ int xx=find(i),yy=find(0); if(xx!=yy)f[xx]=yy; } if(a[i].z+r>=h){ int xx=find(i),yy=find(n+1); if(xx!=yy)f[xx]=yy; } } if(find(0)==find(n+1))printf("Yes "); else printf("No "); } return 0; }
T5宝藏
如果我们设dp[s]表示当前选择集合为s时的最小代价,这个状态是有问题的,说不好听点它压根就是错的,因为它压根就没有考虑每个节点的深度。
我们可以这么考虑问题,我们以深度作为阶段,每次从当前选择集合中扩展一些点作为第i层。
这时我们就可以设计状态了,令dp[i][s]表示当前深度做到了i,当前选择集合为s时的最优方案。
转移时可以枚举扩展出哪些点,然后直接转移。
这样的复杂度是3^n*n^2。
但这样太慢,可以利用lowbit等技巧优化常数
Code
#include<iostream> #include<cstdio> #include<cstring> #define N 13 #define inf 0x3f3f3f3f using namespace std; typedef long long ll; int dp[N][1<<N],n,m,x,y,l,e[N][N],ji[1<<N],f[1<<N],b[N],tag[N],be[1<<N],ans; inline int rd(){ int x=0;char c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48);c=getchar(); } return x; } int main(){ n=rd();m=rd(); memset(e,0x3f,sizeof(e)); for(int i=1;i<=m;++i){ x=rd();y=rd();l=rd(); e[x][y]=e[y][x]=min(e[x][y],l); } memset(dp,0x3f,sizeof(dp)); for(int i=0;i<n;++i)ji[1<<i]=i,dp[0][1<<i]=0;ans=inf; ans=min(ans,dp[0][(1<<n)-1]); for(int i=1;i<=n;++i) for(int s=1;s<(1<<n);++s){ int top=0; memset(b,0x3f,sizeof(b)); for(int j=1;j<=n;++j)if(!(s&(1<<j-1))){ tag[top]=1<<j-1; for(int S=s;S;S-=(S&-S))if(e[j][ji[S&-S]+1]!=inf)b[top]=min(b[top],e[j][ji[S&-S]+1]*i); top++; } for(int j=1;j<(1<<top);++j){ f[j]=f[j-(j&-j)]+b[ji[j&-j]]; if(f[j]>=inf)f[j]=inf; be[j]=be[j-(j&-j)]|tag[ji[j&-j]]; dp[i][s|be[j]]=min(dp[i][s|be[j]],dp[i-1][s]+f[j]); } ans=min(ans,dp[i][(1<<n)-1]); } printf("%d",ans); return 0; }
T6列队
Solution
观察到最后一列很特殊,考虑分开维护。
对于每行的前m-1个点开线段树,里面记个size有表示这个位置没人。
那么整体向做移动怎么维护?
其实不用管它,毕竟对于前m-1列只会从后面插入。
那么对于一颗线段树,我们的操作只有从后面插入一个节点,从集合删去一个节点和查找第K个元素。
在最后一列开线段树,操作时分类讨论一下。
注意:n和m不要弄反,初始化size要为m-1不是m!!!
Code
#include<iostream> #include<cstdio> #define N 300002 using namespace std; typedef long long ll; int size[N],T[N],tot,q; long long x,y,n,m; struct node{ll first;bool second;}; struct seg{ll num;int l,r,sum;}tr[N*20]; node find(int &cnt,int l,int r,int k){ if(!cnt)cnt=++tot;tr[cnt].sum++; if(l==r){ if(tr[cnt].num)return node{tr[cnt].num,0}; else return node{l,1}; } int mid=(l+r)>>1,num=mid-l+1-tr[tr[cnt].l].sum; if(num>=k)return find(tr[cnt].l,l,mid,k); else return find(tr[cnt].r,mid+1,r,k-num); } void add(int &cnt,int l,int r,int x,ll y){ if(!cnt)cnt=++tot; if(l==r){tr[cnt].num=y;return;} int mid=(l+r)>>1; if(mid>=x)add(tr[cnt].l,l,mid,x,y); else add(tr[cnt].r,mid+1,r,x,y); } inline int rd(){ int x=0;char c=getchar();while(!isdigit(c))c=getchar(); while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();return x; } int main(){ n=rd();m=rd();q=rd(); for(int i=1;i<=n;++i)size[i]=m-1;//care size[n+1]=n; for(int i=1;i<=q;++i){ x=rd();y=rd(); if(y==m){ node tmp=find(T[n+1],1,n+q,x); ll num=tmp.first; if(tmp.second)num=num*m; printf("%lld ",num); add(T[n+1],1,n+q,++size[n+1],num); } else{ node tmp=find(T[x],1,m+q,y); ll num=tmp.first; if(tmp.second)num=num+(x-1)*m; printf("%lld ",num); tmp=find(T[n+1],1,n+q,x); ll num2=tmp.first; if(tmp.second)num2=num2*m; add(T[x],1,m+q,++size[x],num2); add(T[n+1],1,n+q,++size[n+1],num); } } return 0; }