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();
}
}
京公网安备 11010502036488号