[XUPT]2020寒假训练---比赛专题

比赛链接:https://vjudge.net/contest/357216
说明: 比赛难度正好符合寒假训练的同学。(有一两道可能一些同学做过,我们出题没考虑到sorry)

下面对题目进行解答一下。(题目方法不唯一,给出的解法仅供参考)

A题
硬核签到不多说了。

B
题意:在一个n*m的图中找到
1.长度不是零。
2.线段的两个端点是网格点。
3.线段的中点是网格点。
如此的线段数量。
思路
可以先考虑一段的时候可以发现以一个点为中点做延伸。实质上是n-2+n-4+n-6+…
横竖本质相同,可以组成一个矩形(包括正方形)。可以在横中选择一个竖中选择一个做组合(C(n,1)*C(m,1))产生两个对角线。

typedef long long ll;
int main()
{
    ll n,m;
    cin>>n>>m;
    ll sum1=0,sum2=0;
    ll temp1=n+1,temp2=m+1,temp=2;
    while(temp1-temp>0)
    {
        sum1+=temp1-temp;
        temp+=2;
    }
    temp=2;
    while(temp2-temp>0)
    {
        sum2+=temp2-temp;
        temp+=2;
    }
    cout<<(n+1)*sum2+(m+1)*sum1+sum1*sum2*2;
    return 0;
}

C题
题意:题意就是输入一个字符输出他的键盘前一位。
思路: 可以预先一个数组存下所有的字符,之后按题意输出即可。

D题
题意:找到丢失的数字。
思路:直接遍历一遍找到最大值和最小值,由于题目中说明了是不一样的,相减即可。

typedef long long ll;
int main()
{
    ll MIN=99999999999;
    ll MAX=-99999999999;
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        ll temp;
        cin>>temp;
        MAX=max(temp,MAX);
        MIN=min(MIN,temp);
    }
    cout<<MAX-MIN+1-n;
	return 0;
}

E题
直接矩阵相加不多说了。

F题
题意:问01组成的数字是否满足条件。
*思路: dfs尝试每一位放0或者1,符合条件退出,注意用long long 和long long 的范围

int flag,n;
void dfs(long long int a,int x)
{
	if(x>19||flag==1)
		return;
	if(a%n==0)
	{
		flag=1;
		cout<<a<<endl;
		return;
	}
	dfs(a*10,x+1);
	dfs(a*10+1,x+1);
}
int main()
{
	while(scanf("%d",&n),n!=0)
	{
		flag=0;
		dfs(1,1);
	}
 } 

G题
题意:变换每一位上的数字,变完的数字也要是素数。
思路:直接队每一位上的数字进行搜索即可。

set<int> s;
typedef pair<int,int> P;
bool isPrime(int x)
{
	if(x==2||x==3)
		return true;
	if(x%6!=1&&x%6!=5)
		return false;
	int temp=sqrt(x);
	for(int i=5;i<=temp;i+=6)
		if(x%i==0||x%(i+2)==0)
			return false;
	return true;
}
int main()
{
	for(int i=1000;i<=9999;i++)
		if(isPrime(i))
			s.insert(i);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n,m;
		cin>>n>>m;
		queue<pair<int,int> > q;
		set<int> book;
		q.push(P(n,0));
		book.insert(n);
		while(!q.empty())
		{
			P temp=q.front();
			int l=temp.first;
			int r=temp.second;
			q.pop();
			if(l==m)
			{
				cout<<r<<endl;
				break;
			}
			//printf("%d\n",l);
			for(int i=0;i<=9;i++)
			{
				int ge,sh,ba,qi;
				ge=l%10,sh=l/10%10,ba=l/100%10,qi=l/1000;
				if(s.find(qi*1000+ba*100+sh*10+i)!=s.end()&&book.find(qi*1000+ba*100+sh*10+i)==book.end())
					q.push(P(qi*1000+ba*100+sh*10+i,r+1)),book.insert(qi*1000+ba*100+sh*10+i);
				if(s.find(qi*1000+ba*100+i*10+ge)!=s.end()&&book.find(qi*1000+ba*100+i*10+ge)==book.end())
					q.push(P(qi*1000+ba*100+i*10+ge,r+1)),book.insert(qi*1000+ba*100+i*10+ge);
				if(s.find(qi*1000+i*100+sh*10+ge)!=s.end()&&book.find(qi*1000+i*100+sh*10+ge)==book.end())
					q.push(P(qi*1000+i*100+sh*10+ge,r+1)),book.insert(qi*1000+i*100+sh*10+ge);
				if(s.find(i*1000+ba*100+sh*10+ge)!=s.end()&&book.find(i*1000+ba*100+sh*10+ge)==book.end())
					q.push(P(i*1000+ba*100+sh*10+ge,r+1)),book.insert(i*1000+ba*100+sh*10+ge);
			}
		}
		 
	}
 } 

H题
题意:给定一个素数,找出有多少种连续素数和等于他。
思路:由于题目中要求的是连续的素数和,直接尺取进行判断即可,预处理出10000内的素数(筛法有多种,本质就是利用素因数做倍数筛即可)。

typedef long long ll;
bool vis[10005];
int prime[10005];
int tot=0;
void init()
{
	memset(vis,false,sizeof(vis));
	ll i,j;
	for(i=2;i<=10005;i++)
	{
		if(!vis[i])
		{
			prime[tot++]=i;
		}
        for(int j=0;j<tot;j++)
        {
            if(i*prime[j]>10005)
                break;
            vis[i*prime[j]]=1;
                if(i%prime[j]==0)
                break;
        }
	}
}
int main()
{
    int n;
    init();
    while(scanf("%d",&n)&&n)
    {
        int sum=0;
        int h=0,t=0;
        int cnt=0;
        while(1)
        {
            while(prime[t]<=n&&sum<n)
            {
                sum+=prime[t++];
            }
            if(sum==n)
            cnt++;
            sum-=prime[h++];
            if(sum<=0)
            break;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

I题
题意:求最长的公共子序列的个数。
思路: 动态规划求解。dp[i][j]表示的是前i个s1字符串 前j个s2字符串 的最长公共子序列。由最优子结构推导。

char s1[1010],s2[1010];
int dp[1010][1010];
int main()
{
	while(scanf("%s",s1+1)!=EOF)
	{
        int i,j,k;
		scanf("%s",s2+1);
		memset(dp,0,sizeof(dp));
		int len1=strlen(s1+1);
		int len2=strlen(s2+1);
		for(i=1;i<=len1;i++)
		{
			for(j=1;j<=len2;j++)
			{
				if(s1[i]==s2[j])
				{
					dp[i][j]=max(dp[i-1][j-1]+1,dp[i][j]);	
				}
				dp[i][j]=max(dp[i][j],max(dp[i][j-1],dp[i-1][j]));
			}
		}	
		/*	for(i=1;i<=len1;i++)
		{
			for(j=1;j<=len2;j++)
			{
				printf("%d ",dp[i][j]);
			}
			printf("\n");
		}*/
		printf("%d\n",dp[len1][len2]);
	}
	return 0;
}

J题
直接按照题意给定的具体步骤进行简单模拟即可。

K题
题意:询问最少数量的大炮能够覆盖到小岛,大炮的范围以d为半径的圆。
思路:可以转化一下,把小岛看做中心,d看做半径画圆,能够覆盖的话将与x轴产生交点,也就是大炮在该区间必须要有点,这样就转化到x轴上,之后进行贪心。

struct A {
    double left,rigth;
}a[1010];
bool cmp(A t1,A t2)
{
    return t1.left<t2.left;
}
int main()
{
    int n,d,i,j,k;
    int yy=1;
    while(scanf("%d %d",&n,&d)&&(n||d))
    {
        bool flag=true;
        for(i=0;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            if(d<y)
            {
                flag=false;
                continue;
            }
            a[i].left=1.0*x-sqrt(d*d*1.0-y*y*1.0);
            a[i].rigth=x*1.0+sqrt(d*d*1.0-y*y*1.0);
        }
        int cnt=-1;
        if(flag)
        {
            sort(a,a+n,cmp);//从小到大排序
            cnt=1;
            double temp=a[0].rigth;
            for(i=1;i<n;i++)
            {
                if(a[i].left>temp)
                {
                    temp=a[i].rigth;
                    cnt++;
                }
                else if(a[i].rigth<temp)
                {
                    temp=a[i].rigth;
                }
            }
            printf("Case %d: %d\n",yy++,cnt);
            continue;
        }
         printf("Case %d: %d\n",yy++,cnt);
    }
    return 0;
}

L题
这道题目有点超出同学们寒假学习的内容。
考虑到题目的范围,需要用到线段树去进行更新。
利用二分去枚举了区间位置。

#define lson (q<<1)
#define rson (q<<1|1)
#define mid ((l+r)>>1)
const int maxn = 100000+10;

int segt[maxn<<2];
int book[maxn];
int len[maxn<<2];
int lazy[maxn<<2];
int T,m,n,A,F,B;

int init(){
    memset(segt,0,sizeof(segt));
    memset(book,0,sizeof(book));
    memset(len,0,sizeof(len));
    memset(lazy,0,sizeof(lazy));
}

void push_up(int q){
    segt[q] = segt[lson] + segt[rson];
}

void build(int q,int l,int r){
    len[q] = r-l+1;
    lazy[q] = -1;
    if(l == r){
        segt[q] = 1;
        return;
    }
    int m = mid;
    build(lson,l,m);
    build(rson,m+1,r);
    push_up(q);
}

void push_down(int q){
    if(lazy[q] != -1){
        lazy[lson] = lazy[rson] = lazy[q];
        segt[lson] = len[lson]*lazy[q];
        segt[rson] = len[rson]*lazy[q];
        lazy[q] = -1;
    }
}

void update(int q,int l,int r,int lhs,int rhs,int k){
    if(l>=lhs && r<=rhs){
        lazy[q] = k;
        segt[q] = len[q]*k;
        return;
    }
    push_down(q);
    int m = mid;
    if(lhs <= m) update(lson,l,m,lhs,rhs,k);
    if(rhs  > m) update(rson,m+1,r,lhs,rhs,k);
    push_up(q);
}

int query(int q,int l,int r,int lhs,int rhs){
    if(l>=lhs && r<=rhs){
        return segt[q];
    }
    int m = mid;
    int ans = 0;
    push_down(q);
    if(lhs <= m) ans+=query(lson,l,m,lhs,rhs);
    if(rhs  > m) ans+=query(rson,m+1,r,lhs,rhs);
    return ans;
}

int find_lift(int l){
    int r = m;
    while(l < r){
        int MID = (l+r)>>1;
        if(query(1,1,m,l,MID) >= 1) r = MID;
        else l = MID+1;
    }
    return r;
}

int find_right(int ll,int B){
    int l = ll, r = m;
    while(l < r){
        int MID = (l+r)>>1;
        if(query(1,1,m,ll,MID) >= B) r = MID; //二分这里面的其实参数不变是精髓 这很重要
        else l = MID + 1;
    }
    return r;
}

int main(){
    std::ios::sync_with_stdio(false);
    cin >> T;
    while(T--){
        init();
        cin >> m >> n;
        build(1,1,m);
        for(int i=0;i<n;++i){
            cin >> F >> A >> B;
            if(F == 1){
                ++A;
                int sum = query(1,1,m,A,m);
                if(sum == 0){
                    cout << "Can not put any one.\n";
                    continue;
                }
                int lift = find_lift(A);
                int right = find_right(lift,min(sum,B));
                cout << lift-1 << " " << right-1 << endl;
                update(1,1,m,lift,right,0);
            }else{
                ++A,++B;
                cout << (B-A+1) - query(1,1,m,A,B) << endl;
                update(1,1,m,A,B,1);
            }
        }
        cout << endl;
    } 
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值