2019 Multi-University Training Contest 8
1003 Acesrc and Good Numbers
题目链接
题意:定义F(d,n)为从1—n中包含d的个数,求给你d和x,求寻找最大的n小于x且满足F(d,n)=n。
思路:对于F(d,n)我们可以通过数位DP得到,设ans=F(d,n),设sub=|n-ans|代表从1—n中d的个数总和与现在数的差值。可知减少每个数至多减少18个d(999—>998减少了3个9),故sub/18可以看作代表至多减少的d的个数。
题解中证明这样的数只存在在1e11内。oeis上也可以找到满足的数据列表。
题解想法是在暴力搜索的基础上进行剪枝。
#include <bits/stdc++.h> #define maxn 20 typedef long long ll; using namespace std; int d;ll x; ll ans=0; int cnt; int lim[maxn]; ll p[maxn]; ll num[maxn]; ll calc(int pos){ if(pos<=0)return 0; return 1ll*pos*p[pos-1]; } void dfs(int d,int index){ if(index==0)return; for(int i=0;i<lim[index];i++){ if(i==d) ans+=p[index-1]; ans+=calc(index-1); } if(lim[index]==d){ ans+=num[index-1]+1; } dfs(d,index-1); } void solve(int d,ll x){ ans=0; cnt=0; p[0]=1; while(x){ lim[++cnt]=x%10; p[cnt]=p[cnt-1]*10; x/=10; } for(int i=1;i<=cnt;i++) num[i]=num[i-1]+lim[i]*p[i-1]; dfs(d,cnt); } void input(){ scanf("%d%lld",&d,&x); solve(d,x); while(ans!=x){ x-=1ll*ceil(1.0*abs(ans-x)/18); solve(d,x); } printf("%lld\n",x); } int main(){ int t; scanf("%d",&t); while(t--){ input(); } }
1008 Andy and Maze
题目链接
题意:给出一张图,n个点,m条边。寻找从任意点出发,走k步的最大边权和。
思路:对于每一个点进行搜索,记录所有边中的最大边权,当现边权和+剩余步数*最大边权<=已经记录的最大边权和,就剪枝。
参考
不会算此解法的时间复杂度。
#include <bits/stdc++.h> #define maxn 10005 using namespace std; int n,m,k; int ans,maxx; struct edge{ int to,cost; }; vector <edge>G[maxn]; int vis[maxn]; void dfs(int x,int step,int tot){ if(step==k) { ans=max(ans,tot); return; } if(tot+(k-step)*maxx<=ans) return; for(int i=0;i<G[x].size();i++){ if(!vis[G[x][i].to]){ vis[G[x][i].to]=1; dfs(G[x][i].to,step+1,tot+G[x][i].cost); vis[G[x][i].to]=0; } } } void input(){ scanf("%d%d%d",&n,&m,&k); int i; for(i=1;i<=n;i++){ G[i].clear(); vis[i]=0; } ans=maxx=-1; int u,v,w; for(i=1;i<=m;i++){ scanf("%d%d%d",&u,&v,&w); G[u].emplace_back((edge){v,w}); G[v].emplace_back((edge){u,w}); maxx=max(maxx,w); } } void solve(){ for(int i=1;i<=n;i++){ vis[i]=1; dfs(i,1,0); vis[i]=0; } if(ans==-1) printf("impossible\n"); else printf("%d\n",ans); } int main(){ int t; scanf("%d",&t); while(t--){ input(); solve(); } }
1009 Calabash and Landlord
题目链接
题意:在一个无限大小的平面用两个矩形框出若干给联通块,求连通块个数。
思路:将坐标哈希后,对于两个联通块涂色,计算同颜色的联通块个数,最后加一。哈希后的点坐标要转化为块的坐标。
#include <bits/stdc++.h> using namespace std; int x[4],y[4]; int xCopy[4],yCopy[4]; int sizeX,sizeY; int g[5][5]; int book[5][5]; void input(){ scanf("%d%d%d%d",&x[0],&y[0],&x[1],&y[1]); scanf("%d%d%d%d",&x[2],&y[2],&x[3],&y[3]); } void Init_Hash_x(){ int i; for(i=0;i<4;i++) xCopy[i]=x[i]; sort(xCopy,xCopy+4); sizeX=unique(xCopy,xCopy+4)-xCopy; } int Hash_x(int x){ return lower_bound(xCopy,xCopy+sizeX,x)-xCopy+1; } void Init_Hash_y(){ int i; for(i=0;i<4;i++) yCopy[i]=y[i]; sort(yCopy,yCopy+4); sizeY=unique(yCopy,yCopy+4)-yCopy; } int Hash_y(int x){ return lower_bound(yCopy,yCopy+sizeY,x)-yCopy+1; } struct node{ int x,y; }; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; void BFS(int x,int y){ book[x][y]=1; queue <node>q; node temp; temp.x=x;temp.y=y; q.push(temp); while(!q.empty()){ node f=q.front(); q.pop(); for(int i=0;i<4;i++){ int tx=f.x+dx[i]; int ty=f.y+dy[i]; if(tx>=1&&tx<=sizeX&&ty>=1&&ty<=sizeY&&!book[tx][ty]&&g[tx][ty]==g[f.x][f.y]) { book[tx][ty]=1; temp.x=tx;temp.y=ty; q.push(temp); } } } } void solve(){ Init_Hash_x(); Init_Hash_y(); int i,j; // cout<<endl; // for(i=0;i<4;i++) // cout<<Hash_x(x[i])<<' '<<Hash_y(y[i])<<endl; // cout<<endl; memset(g,0,sizeof g); for(i=Hash_x(x[0]);i<Hash_x(x[1]);i++) for(j=Hash_y(y[0]);j<Hash_y(y[1]);j++) g[i][j]+=1; for(i=Hash_x(x[2]);i<Hash_x(x[3]);i++) for(j=Hash_y(y[2]);j<Hash_y(y[3]);j++) g[i][j]+=2; // for(i=1;i<=4;i++){ // for(j=1;j<=4;j++) // cout<<g[i][j]; // cout<<endl; // } memset(book,0,sizeof book); int ans=1; for(i=1;i<=4;i++) for(j=1;j<=4;j++) if(!book[i][j]&&g[i][j]) { // cout<<i<<' '<<j<<endl; BFS(i,j); ans++; } printf("%d\n",ans); } int main(){ int t; scanf("%d",&t); while(t--){ input(); solve(); } }
1010 Quailty and CCPC
题目链接
题意:比赛规定了d*10%的队伍数可以归为金牌,当这个金牌队伍数num的小数部分为0.5时,我们可以对第num+1的队伍进行申请,让其从银牌变为金牌。题目求是否存在这样队伍,输出其名称
思路:排序后,判断小数部分是否为0.5,找到队伍名即可。
#include <bits/stdc++.h> #define maxn 100005 typedef long long ll; using namespace std; int n,d; struct team{ char name[20]; int p,t; }te[maxn]; bool cmp(team a,team b){ if(a.p==b.p) return a.t<b.t; return a.p>b.p; } void input(){ int i; scanf("%d%d",&n,&d); for(i=0;i<n;i++){ scanf("%s%d%d",&te[i].name,&te[i].p,&te[i].t); } sort(te,te+n,cmp); } bool check(int n,int d){ ll ans=1ll*n*d; if(ans%10==5) return 1; else return 0; } void solve(){ int num=n*d/10; if(check(n,d)){ printf("%s\n",te[num]); }else printf("Quailty is very great\n"); } int main(){ int t; scanf("%d",&t); while(t--){ input(); solve(); } }
1011 Roundgod and Milk Tea
题目链接
题意:每个班级有ai个人制作bi个奶茶,每个班的人只能喝别班的奶茶,求最后能喝的总人数
思路:记sum为所有班人数总和,对于每个班制作的奶茶bi,其能分给sum-ai的人。我们取min(bi,sum-ai)代表每个班分给其他班人的奶茶数,其相加得到总和SUM,代表我一共分出的奶茶数,当SUM>=sum时代表,分出的奶茶数大于所有班人数,故答案应为sum。当SUM<sum时,代表我分出的奶茶数小于所有班的人数,其中可能存在重复部分,但可以通过更改分配方式(二分图最优匹配)使得分出的奶茶数都最优,题目不要求寻找这种匹配方式,只需知道分出的奶茶数即为所求答案。
#include <bits/stdc++.h> #define maxn 1000005 #define INF 0x3f3f3f3f typedef long long ll; using namespace std; int n; int a[maxn],b[maxn]; ll sum; void input(){ int i; scanf("%d",&n); sum=0; for(i=1;i<=n;i++){ scanf("%d%d",&a[i],&b[i]); sum+=a[i]; } } void solve(){ ll ans=0; int i; for(i=1;i<=n;i++){ sum-=a[i]; ans+=min(1ll*b[i],sum); sum+=a[i]; } ans=min(ans,sum); printf("%lld\n",ans); } int main(){ int t; scanf("%d",&t); while(t--){ input(); solve(); } }