题意:n条线段[li,ri],f[l,r]为线段[l,r]的长度,问任意k条线段交集长度的累加和?
n,k<=2e5,-1e9<=li,ri<=1e9.
若点p被m条线段覆盖,则对答案贡献C(m,k),点的数量很多,转为计算f[i]:正好被i条线段覆盖的点有多少个?
题意:起点为1,从点i到点j花费|i-j|,有n条捷径a[i]代表从i->a[i]花费1,其中i<=a[i]<=a[i+1].
n<=2e5,问从1出发到k的最短距离 k=1,2,3...n.
建图:i-j连边距离为|i-j|,i->a[i]连边距离为1.
这样边数太大,因为任意一个距离>1的边 都可以用若干条相邻边来代替,所以只连相邻边即可.
图中每条边代价都为1,跑一遍BFS即可.
n,k<=2e5,-1e9<=li,ri<=1e9.
若点p被m条线段覆盖,则对答案贡献C(m,k),点的数量很多,转为计算f[i]:正好被i条线段覆盖的点有多少个?
答案为:f[i]*C(i,k)(i=k..n),端点最多2n个,利用前缀差分.一段一段算即可(同一段中的点被覆盖次数相同)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=2e5+20;
ll n,k,l,r,f[N],inv[N];
ll powmod(ll x,ll n)
{
ll s=1;
while(n)
{
if(n&1)
s=(s*x)%mod;
n>>=1,x=(x*x)%mod;
}
return s;
}
void init()
{
f[0]=inv[0]=1;
for(int i=1;i<N;i++)
f[i]=(f[i-1]*i)%mod,inv[i]=powmod(f[i],mod-2)%mod;
}
ll Comb(ll n,ll k)
{
ll res=(f[n]*inv[n-k])%mod;
return (res*inv[k])%mod;
}
map<ll,ll> mp;
int main()
{
init();
while(cin>>n>>k)
{
mp.clear();
for(int i=0;i<n;i++)
{
scanf("%I64d%I64d",&l,&r);
mp[l]++,mp[r+1]--;
}
int l=mp.begin()->first;
ll sum=0,ans=0;
map<ll,ll>::iterator it;
for(it=mp.begin();it!=mp.end();it++)
{
ll num=it->first-l;
if(sum>=k)
ans=(ans+(num*Comb(sum,k))%mod)%mod;
sum+=it->second;
l=it->first;
}
printf("%lld\n",ans);
}
return 0;
}
P.A
把0改成11,枚举起点后,可以由序列相邻点算出x,y方向偏差值,判断是否合法即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+20;
int x[N],y[N],n;
string s;
bool check(int x,int y)
{
if(x<0||x>=3)
return false;
if(y<0||y>=4)
return false;
if(y==3&&x!=1)
return false;
return true;
}
int main()
{
while(cin>>n>>s)
{
int t;
for(int i=0;s[i];i++)
{
if(s[i]=='0')
t=11;
else
t=s[i]-'0';
t--;
y[i]=t/3;
x[i]=t%3;
}
int cnt=0;
for(int i=0;i<3;i++)
{
for(int j=0;j<4;j++)
{
int tx=i,ty=j,ok=1;
ok&=check(tx,ty);
for(int k=0;k<n-1;k++)
{
tx+=x[k+1]-x[k];
ty+=y[k+1]-y[k];
ok&=check(tx,ty);
}
if(ok)
cnt++;
}
}
//cout<<cnt<<endl;
if(cnt==1)
puts("YES");
else
puts("NO");
}
return 0;
}
P.B
题意:起点为1,从点i到点j花费|i-j|,有n条捷径a[i]代表从i->a[i]花费1,其中i<=a[i]<=a[i+1].
n<=2e5,问从1出发到k的最短距离 k=1,2,3...n.
建图:i-j连边距离为|i-j|,i->a[i]连边距离为1.
这样边数太大,因为任意一个距离>1的边 都可以用若干条相邻边来代替,所以只连相邻边即可.
图中每条边代价都为1,跑一遍BFS即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+20;
const int inf=0x3f;
int n,a[N],dis[N];
vector<int> e[5*N];
queue<int> q;
void bfs(int s)
{
memset(dis,inf,sizeof(dis));
dis[s]=0,q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i];
if(dis[v]>dis[u]+1)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
}
int main()
{
while(cin>>n)
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(i!=a[i])
e[i].push_back(a[i]);
if(i!=n)
e[i].push_back(i+1);
if(i!=1)
e[i].push_back(i-1);
}
bfs(1);
for(int i=1;i<=n;i++)
printf("%d%c",dis[i],i==n?'\n':' ');
}
return 0;
}
P.D
题意:两个序列a,b.问有多少个区间[l,r]满足max(a[l],a[l+1]..a[r])=min(b[l],b[l+1]..b[r])?
n<=2e5,a[i],b[i]<=1e9.
枚举区间的开头L,r越大,a的最大值越大,b的最小值越小.结尾点有单调性.
rmq预处理后 枚举L,二分r:最后一次mx<mn,第一次mx>mn.则这两个中间的就为mx==mn
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+20;
int n,a[N],b[N];
int mx[N][32],mn[N][32];
void init()
{
for(int i=1;i<=n;i++)
mx[i][0]=a[i],mn[i][0]=b[i];
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l,int r,int op)
{
int k=0;
while(r-l+1>=(1<<(k+1)))
k++;
if(op==1)
return max(mx[l][k],mx[r-(1<<k)+1][k]);
return min(mn[l][k],mn[r-(1<<k)+1][k]);
}
int main()
{
while(cin>>n)
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
init();
ll ans=0;
for(int i=1;i<=n;i++)
{
int l=i,r=n,r1,r2;
while(l<=r)
{
int mid=l+r>>1;
int c=query(i,mid,1),d=query(i,mid,0);
if(c<d)
l=mid+1;
else
r=mid-1;
}
r1=l-1;
l=i,r=n;
while(l<=r)
{
int mid=l+r>>1;
int c=query(i,mid,1),d=query(i,mid,0);
if(c>d)
r=mid-1;
else
l=mid+1;
}
r2=r+1;
ans+=max(0,(r2-r1-1));
}
printf("%lld\n",ans);
}
return 0;
}