暑假周赛2(线段树+树状数组)

唉,我真是菜的抠脚啊

B - Paths in a Complete Binary Tree

 CodeForces - 792D 

思路:

这题感觉就是找规律,模拟即可,没什么算法,反正我也做不出来可能,嘿嘿。

但是数据范围很大,必须要用位运算,而且注意1的地方用 1ll。

主要就是当遇到U时,判断当前结点是左孩子还是右孩子。(d自叶子往上从0开始编号)

看了别人写的是这样判断的 :flag = ((ans - (1ll << d)) / (1ll << (d + 1))) % 2;

我也没怎么理解其实,唉。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,q,ans;
char s[1000100];
int main()
{
	scanf("%I64d%I64d",&n,&q);
	char c;
	while(q--)
	{
		scanf("%I64d%s",&ans,s);
		ll p=(n+1)>>1ll;
		ll d=log2(p);
		while(ans!=p){
			if(ans>p){
				p+=1ll<<(d-1);
				d--;
			}
			else{
				p-=1ll<<(d-1);
				d--;
			}
		}
		int l=strlen(s);
		for(int i=0;i<l;i++)
		{
			if(s[i]=='U')
			{
				ll flag = ((ans - (1ll << d)) / (1ll << (d + 1))) % 2;
				if(flag){	
					ans-=(1ll<<d);
					d++;
				}
				else if(!flag&&(1ll<<(d+1)<=n)){
					ans+=(1ll<<d);
					d++;
				}
			}
			else if(s[i]=='L')
			{
				if(d>0){
					ans-=(1ll<<(d-1));
					d--;
				}
			}
			else if(s[i]=='R')
			{
				if(d>0){
					ans+=(1ll<<(d-1));
					d--;
				}
			}
		}
		printf("%I64d\n",ans);
	}
	return 0;
}

D - Make Palindrome

 CodeForces - 600C 

题目大意:

给定一个字符串,用最少的次数将其变成回文串,字母改变位置不算操作次数。

思路:

记录每个字母出现的次数,然后从a开始往后扫描,遇到偶数跳过,遇到奇数就从z开始往前找到奇数个的字母,将其中的一个字母改变。

注意如果是只有一个奇数个字母,则无需改变了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2*1e5+10;
int main()
{
    char s[maxn],ss[maxn];
    int a[30]={0};
    scanf("%s",s);
    int l=strlen(s);
    for(int i=0;i<l;i++)
        a[s[i]-'a']++;
    int cnt=0;
    for(int i=0;i<26;i++)  //找到有几个字母为奇数个
        if(a[i]%2)
            cnt++;
    for(int i=0;i<26;i++)
    {
        if(a[i]%2)
        {
            if(cnt>1)       //如果有一个以上的奇数个字母,先将字典序大的奇数个字母变成字典序小的
            {
                for(int j=25;j>i;j--)
                {
                    if(a[j]%2)
                    {
                        a[i]++;
                        a[j]--;
                        cnt-=2;
                        break;
                    }
                }
            }
        }
    }
    int flag=-1;
    for(int i=0;i<26;i++)
    {
        if(a[i]%2)
        {
            flag=i;
            a[i]--;
            break;
        }
    }
    for(int i=0;i<26;i++)
    {
        int k=a[i]/2;
        while(k--)printf("%c",'a'+i);
    }
    if(flag!=-1)printf("%c",'a'+flag);
    for(int i=25;i>=0;i--)
    {
        int k=a[i]/2;
        while(k--)printf("%c",'a'+i);
    }
    return 0;
}

 

F - A Magic Lamp

 HDU - 3183 

题目大意:

给定一串数字,使在去掉m个数字后得到的数最小。

思路:

一开始老是想着去掉n - m个最大的数,其实这种思路是完全不对的,比如561289 2,最小应该是1289;

然后发现其实完全可以把其中最小的 m 个数找出来组成一个最小的数。

介于举的例子的情况,应该当需要取出m个最小数时,下一个要取的数是从当前位置 i 开始 n - m 个数中的最小值。

静态表查询最值可用ST表实现。

#include<bits/stdc++.h>
using namespace std;
int dp[1010][10];
char s[1010];
int Min(int x,int y)
{
    if(s[x]<=s[y])return x;
    else return y;
}
void rmq(int n)
{
    for(int i=0;i<n;i++)
        dp[i][0]=i;
    for(int j=1;(1<<j)<n;j++){
        for(int i=0;i+(1<<j)-1<n;i++){
            dp[i][j]=Min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
}
int query(int l,int r)        //求l到r范围内的最小值
{
    int k=(int)(log(r-l+1)/log(2));
    return Min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
    int m,a[1010];
    while(cin>>s>>m)
    {
        int l=strlen(s),ma;
        rmq(l);
        m=l-m;
        int i=0,cnt=0;
        while(m--)       
        {
            i=query(i,l-m-1);     //m--,所以区间长度要+1
            a[cnt]=s[i];
            i++;cnt++;
        }
        for(i=0;i<cnt;i++)
            if(a[i]!='0')
                break;
        if(i==cnt)printf("0");
        else{
            while(i<cnt)
                printf("%c",a[i++]);
        }
        printf("\n");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值