2015 Multi-University Training Contest 4

官方题解:2015 Multi-University Training Contest 4 solutions BY 学军中学


1001

题意:每次询问\([a,b](a\leqslant b)\)中有多少个 beautiful number, beautiful number指每位数字不同
\( 1\leqslant T\leqslant 1000,1\leqslant a\leqslant b\leqslant 10^5\)

因为范围比较小,可以直接暴力预处理出[1,n]内beautiful number的数量。
[a,b]=[1,a]-[1,b-1]

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define LL __int64
#define N 100005
#define INF 1<<30

int cnt[N];
int vis[10];
void init(){
    memset(cnt,0,sizeof(cnt));
    int t,flag;
    for(int i=1;i<=100000;++i){
        t=i;
        flag=0;
        memset(vis,0,sizeof(vis));
        while(t){
            vis[t%10]++;
            if(vis[t%10]>1){
                flag=1;
                break;
            }
            t/=10;
        }
        cnt[i]=cnt[i-1];
        if(flag==0)
            cnt[i]++;
    }
}
int main(){

	int T;
	scanf("%d",&T);
	init();
	while(T--){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",cnt[b]-cnt[a-1]);
	}
	return 0;
}



1002

题意:求出最长的等差数列和等比数列,答案取两者长度大的
\(1\leqslant n\leqslant 10^6\)

随便YY一发就能过。


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define INF 0x3f3f3f3f
#define ll __int64
#define N 1000010

int n;
int a[N];
int dp[N][2];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }

        dp[1][0]=dp[1][1]=1;
        for(int i=2;i<=n;i++) dp[i][0]=dp[i][1]=2;
        for(int i=3;i<=n;i++){
            if(a[i]-a[i-1]==a[i-1]-a[i-2]) dp[i][0]=dp[i-1][0]+1;
            if(a[i]*1.0/a[i-1]==a[i-1]*1.0/a[i-2]) dp[i][1]=dp[i-1][1]+1;
        }

        int ans=0;
        for(int i=1;i<=n;i++){
            ans=max(ans,max(dp[i][0],dp[i][1]));
        }
        printf("%d\n",ans);
    }
    return 0;
}



1007

题意:知道一个序列的不同的连续字串个数K,求构造满足K的任意一个序列
\(1\leqslant K\leqslant 10^9\),序列长度\(1\leqslant K\leqslant 10^5\)

因为序列长度\(n\)的最大范围小于\(K\),所以不能用全填\(1\)的方法

如果序列为这种形式:\(1,\cdots ,1,2\cdots,2,3,\cdots,3,\cdots,t\)
设\(n\)为序列长度,\(L_i\)为数\(i\)出现的次数,那么序列的不同的连续字串个数
$$K=\frac{n^2+n-\sum_{i=1}^{t}L_i(L_i-1)}{2}$$
可以列出下面的方程
$$\left\{\begin{matrix}
\sum_{i=1}^{t}L_i(L_i-1)=n^2+n-2k\\ 
\sum_{i=1}^{t}L_i=n
\end{matrix}\right.$$
设\(\sum_{i=1}^{t}L_i(L_i-1)=d\),那么可以用二分找出最大的\(p\)使\(p(p-1)\leqslant d\),然后\(L_1\)可以取\(p\)
此时另\(d=d-p(p-1)\),又可以用上面的方法求出所有\(L_i\)。因为\(d\)一定为偶数,所以最后一定能让\(d=0\)
因为\(d=n^2+n-2k\),所以需要用二分找到最小的\(n\)满足\(n^2+n-2k\geqslant 0\)
如果最后满足 \(\sum_{i=1}^{t}L_i=n \),那么便是一个解。
通过打表发现不满足的只有\(k=4\)和\(k=16\)时,对于这两组直接全输出1就行

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define LL __int64
#define N 100005
#define INF 1<<30

int L[N];
int cnt;

int main(){

	LL k;
	LL n;
	while(~scanf("%I64d",&k)){
        cnt=0;
        
	    if(k==4||k==16){
            printf("%d\n",k);
            while(k--){
                printf("1");
                if(k!=0)printf(" ");
            }
            printf("\n");
            continue;
	    }
	    

        //cal min(n) :n^2+n-2k>=0
        LL l=1,r=100000,m;
        while(l<=r){
            m=(l+r)>>1;
            if(m*m+m-2*k>=0){
                n=m;
                r=m-1;
            }else{
                l=m+1;
            }
        }

        //cal L[]
        LL d=n*n+n-2*k;
        LL p;
        LL len=0;
        while(d||len<n){
            l=1,r=100000;
            while(l<=r){
                m=(l+r)>>1;
                if(m*(m-1)<=d){
                    p=m;
                    l=m+1;
                }else{
                    r=m-1;
                }
            }
            if(p==0)p=1;
            L[++cnt]=(p?p:1);
            len+=p;
            d=d-p*(p-1);
        }

        printf("%I64d\n",n);
        int first=true;
        for(int i=1;i<=cnt;++i){
            n-=L[i];
            while(L[i]--){
                if(!first)printf(" ");
                printf("%d",i);
                first=false;
            }
        }
        printf("\n");

	}
	return 0;
}



1010

题意:在一个c*r的格子里,有一些水滴。一个大小大于4的水滴会向4个方向(上下左右)分裂成4个小水滴。小水滴会一直沿分裂方向运动,直到遇到水滴,使水滴大小加1,小水滴消失。现在给出一些水滴的坐标和大小。并在0s时在(x,y)处有一个水滴分裂。问Ts时所有水滴的状态
\( 1\leqslant r \leqslant100, 1\leqslant c\leqslant 100, 1\leqslant n\leqslant 100, 1\leqslant T\leqslant 10000 \)

因为数据范围较小,可以直接模拟整个过程。

有三点需要注意:
1、无论分裂前的水滴大小是多少,分裂后小水滴的大小始终是为1的。
2、0s时注意判断是否有水滴大小大于4,这些水滴在0s分裂。
3、水滴分裂后及时清除水滴的标记,此时水滴已经不存在

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define LL __int64
#define N 105
#define INF 1<<30

struct node1 {
    int x,y;
    node1() {}
    node1(int _x,int _y) {
        x=_x;
        y=_y;
    }
} wd[N];

struct node2 {
    int x,y,d;
    node2() {}
    node2(int _x,int _y,int _d) {
        x=_x;
        y=_y;
        d=_d;
    }
};

int mpt[N][N];
int val[N][N];
int dir[4][2]= {0,1,1,0,0,-1,-1,0};
int ans[N];
int r,c,n,T;

queue<node2> sd;
vector<node2> temp;

void init() {
    memset(mpt,0,sizeof(mpt));
    memset(val,0,sizeof(val));
    memset(ans,-1,sizeof(ans));
    while(!sd.empty())sd.pop();
    temp.clear();
}

//判断是否超出边界
bool judge(node2 a) {
    if(a.x<1||a.x>r||a.y<1||a.y>c)return false;
    return true;
}

//判断当前位置是否为水滴
bool iswd(node2 a) {
    if(mpt[a.x][a.y])return true;
    return false;
}

//水滴分裂
void crack(int t) {

    for(int i=1; i<=n; ++i) {
        if(val[wd[i].x][wd[i].y]>4&&mpt[wd[i].x][wd[i].y]) {
            ans[i]=t;
            for(int j=0; j<4; ++j)
                temp.push_back(node2(wd[i].x,wd[i].y,j));
            val[wd[i].x][wd[i].y]=4;
            mpt[wd[i].x][wd[i].y]=0;
        }
    }
    for(int i=0; i<temp.size(); ++i)
        sd.push(temp[i]);
    temp.clear();
}

//模拟
void bfs(int sx,int sy) {

    node2 now,next;
    for(int i=0; i<4; ++i)
        sd.push(node2(sx,sy,i));
    crack(0);
    val[sx][sy]=4;
    for(int t=1; t<=T; ++t) {
        while(!sd.empty()) {
            now=sd.front();
            sd.pop();
            next.x=now.x+dir[now.d][0];
            next.y=now.y+dir[now.d][1];
            next.d=now.d;
            val[now.x][now.y]--;
            if(!judge(next))continue;
            val[next.x][next.y]++;
            if(iswd(next))continue;
            temp.push_back(next);
        }
        crack(t);
    }
}

int main() {
    int x,y,s;
    while(scanf("%d%d%d%d",&r,&c,&n,&T)!=EOF) {
        init();
        for(int i=1; i<=n; ++i) {
            scanf("%d%d%d",&x,&y,&s);
            mpt[x][y]=1;
            val[x][y]=s;
            wd[i]=node1(x,y);
        }
        scanf("%d%d",&x,&y);
        bfs(x,y);
        for(int i=1; i<=n; ++i) {
            if(ans[i]==-1) {
                printf("1 %d\n",val[wd[i].x][wd[i].y]);
            } else {
                printf("0 %d\n",ans[i]);
            }
        }
    }
    return 0;
}





(待续。。。)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值