题目链接: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;
}