Codeforces Round #587 (Div. 3)

题目链接:https://codeforces.com/contest/1216/problem

C. White Sheet

题解&代码:https://blog.csdn.net/qq_43381887/article/details/101165751

E2 Numerical Sequence (hard version)(二分/模拟)

题意:给定一个序列1 12 123 1234 … 求第k位的值, 1 < = k < = 1 e 18 1<=k<=1e18 1<=k<=1e18
题解:首先确定是哪个位数a规模的,即第k位所在的序列的最大值是几位数的;确定是a位数,确定后在10…0(a位)和9…99(a位)二分出具体是哪个数;确定是b后,从前往后数,按9、90、900数(因为一位数9个,两位数90个…),确定是第k位所在的数是第几位的。确定是位c的后,根据当前剩下多少位数,易得所在数;确定所在数为d后,找出该数值就显然了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=12;
 
ll pre1[maxn],a[maxn];//{0,9,99,999,9999,99999,999999,9999999,99999999,99999999}
ll pre2[maxn];
 
void init()
{
	ll cur=9;
	for(int i=1;i<maxn;i++){
		a[i]=cur+a[i-1];
		cur*=10;
	}
	ll j=9;
	for(int i=1;i<maxn;i++,j*=10){
		pre2[i]=(ll)i*j+pre2[i-1];
	}
	j=9;
	for(int i=1;i<maxn;i++,j*=10){
		pre1[i]=(ll)j*(j+1)/2*i+(ll)j*pre2[i-1];
		pre1[i]+=pre1[i-1];//
		if(pre1[i]>=1000000000000000000LL) break;
	}
}
int cal1(ll &k)
{
	int i=1;
	while(pre1[i]<k) i++;
	k-=pre1[i-1];
	return i;
}
ll cal2(ll &k,int pos)
{
	ll l=1,r=a[pos]-a[pos-1],mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(mid*(mid+1)/2*pos+pre2[pos-1]*mid>=k){
			if(mid*(mid-1)/2*pos+pre2[pos-1]*(mid-1)<k) break;
			else r=mid-1;
		}else{
			l=mid+1;
		}
	}
	k-=(ll)mid*(mid-1)/2*pos+pre2[pos-1]*(mid-1);
	return mid+a[pos-1];
}
int cal3(ll &k,ll val)
{
	int i;
	ll j;
	for(i=1,j=9;j*i<=k;i++,j*=10){
		k-=j*i;//
	}
	return i;
}
ll cal4(ll &k,int pos,ll val)
{
	ll num=a[pos-1],p=k/pos;
	num+=p;
	k-=p*pos;
	if(k) num++;
	return num;
}
int cal_len(ll x)
{
	int res=0;
	while(x) x/=10,res++;
	return res;
}
 
int solve(ll k)
{
	int p=cal1(k);//确定是p位数规模
	ll p2=cal2(k,p);//确定是在具体数p2 
	int p3=cal3(k,p2);//确定在p2序列中是p3位数规模的 
	ll num=cal4(k,p3,p2);//确定所在 是数num 
	int len=cal_len(num);
	if(k==0) k=len;
	k=len+1-k;//从后往前数 k倒一下 
	int res;
	while(k--){
		res=num%10;
		num/=10;
	}
	return res;
}
int main()
{
	init();
	int t;scanf("%d",&t);
	while(t--){
		ll k;
		scanf("%I64d",&k);
		printf("%d\n",solve(k));
	}
	return 0;
}

F - Wi-Fi

题意:给定n个房间,编号1-n,给这n个房间联网,第i个房间联网需要花费i单位,部分房间有路由,可覆盖max(i-k,1)到min(i+k,n),求最小花费。
队友的神仙线段树解法(^O^):

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
#define LL long long
#define lson (i<<1)
#define rson (i<<1|1)
const LL iinf=0x3fffffffffffffff;
const LL inf=0x3fffffffffffffff;
LL dp[maxn];
char s[maxn];
LL n,k1,k;int L,R;int pos;
struct node
{
	int l,r;
	LL val;	//线段树要维护的值(最大值,和等)
}tr[maxn<<2];
void down(int i)//i在这里就是当前的根节点,有些模板用的是rt //很多模板每个函数的传参里面都会带int l,int r其实就是该节点对应的区间左端点和右端点
{
	if(tr[i].val==iinf||tr[i].l==tr[i].r)return;
	if(tr[i].l!=tr[i].r)
	{
		tr[lson].val=min(tr[lson].val,tr[i].val);
		tr[rson].val=min(tr[rson].val,tr[i].val);
	}
	tr[i].val=iinf;//情况标记。注意乘的系数是清为1
}
void build(int  i,int l,int   r)//这里的i只的是对应存储线段树的数组 //很多模板每个函数的传参里面都会带int l,int r其实就是该节点对应的区间左端点和右端点
{
	tr[i].l=l;
	tr[i].r=r;
	if(l==r)
	{
		tr[i].val=dp[l];
		return;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);//i<<1是左儿子
	build(i<<1|1,mid+1,r);//这个是右儿子
	tr[i].val=iinf;
	return;
}

LL query(int  i)//这里就不设l,r了,直接在函数外设pos
{
    if(pos<1)
        return 0;
	down(i);//先下传标记
	//由于这里后面有判断,不需要判断违法的i节点
	if(pos==tr[i].l&&tr[i].r<=pos)
		return tr[i].val;
	int mid=tr[i].l+tr[i].r>>1;
	if(pos<=mid) return query(lson);
	if(mid+1<=pos) return query(rson);
}
void plu(int i)//k就是区间修改的值
{

	down(i);//先传标记
	if(L<=tr[i].l&&tr[i].r<=R)
	{
		tr[i].val=min(tr[i].val,k);return;//k就是询问的参数
	}
	int mid=(tr[i].l+tr[i].r>>1);
	if(L<=mid) plu(lson);
	if(mid+1<=R) plu(rson);
	down(lson),down(rson);
}
int main()
{
    scanf("%d%d",&n,&k1);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        dp[i]=inf;
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        pos=i-1;k=query(1)+i;
        L=R=i;
        plu(1);
        if(s[i]=='1')
        {
            pos=max(i-k1-1,0LL);k=query(1)+i;
            L=max(i-k1,1LL);R=min(i+k1,n);
            plu(1);
        }
    }
    pos=n;
    cout<<query(1);
}

一个聚聚的线段树+dp:https://www.cnblogs.com/starve/p/11572879.html
dp[i][0]:表示第i个位置不装的最小代价
dp[i][1]:表示第i个位置装的最小代价
T1的线段树是维护装的最小代价
T2的线段树是维护装和不装的最小代价

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=200010;
#define inf 0x3f3f3f3f3f3f3f3f

ll dp[maxn][2];
char s[maxn];
struct Tree{
	ll mn[maxn<<2];
	void clr(){
		memset(mn,0x3f,sizeof(mn));
	}
	void pushup(int rt){
		mn[rt]=min(mn[rt<<1],mn[rt<<1|1]);
	}
	void update(int rt,int l,int r,int pos,ll val){
		if(l==r){
			mn[rt]=min(mn[rt],val);return; 
		}
		int m=(l+r)>>1;
		if(pos<=m) update(rt<<1,l,m,pos,val);
		else update(rt<<1|1,m+1,r,pos,val);
		pushup(rt);
	}
	ll query(int rt,int l,int r,int a,int b){
		if(l>=a&&r<=b) return mn[rt];
		int m=(l+r)>>1;
		ll ans=inf;
		if(a<=m) ans=min(ans,query(rt<<1,l,m,a,b));
		if(m<b) ans=min(ans,query(rt<<1|1,m+1,r,a,b));
		return ans;
	}
}T1,T2;
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	scanf("%s",s+2);
	memset(dp,0x3f,sizeof(dp));
	T1.clr();T2.clr();
	dp[1][0]=dp[1][1]=0;
	T2.update(1,1,n,1,0);
	for(int i=2;i<=n+1;i++){
		dp[i][0]=T1.query(1,1,n,max(1,i-k),i-1);
		if(s[i]=='1') 
			dp[i][1]=T2.query(1,1,n,max(1,i-k-1),i-1)+i-1;
		else 
			dp[i][1]=min(min(dp[i-1][0],dp[i-1][1]),T1.query(1,1,n,max(1,i-k-1),i-1))+i-1;
		T2.update(1,1,n,i,min(dp[i][0],dp[i][1]));
		if(s[i]=='1') T1.update(1,1,n,i,dp[i][1]);
	}
	printf("%I64d",min(dp[n+1][0],dp[n+1][1]));
	return 0;
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值