T1:
有n种方块,每种方块有权值和个数。
每次寻找区间[l,r]的权值范围在[a,b]的方块中,选k个搭积木。
问每个积木的最大的权值最小是多少。
n,m<=30000
题解:
问最大的权值最小不就是……
排个序之后的第k个,就是最大的最小权值了啊……
然后所以我们考虑这个东西不就是有权值范围限制的第k大吗……
这不是裸的可持久化线段树吗……
随便搞搞就好了。
#include <algorithm>
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define u t[x]
#define o t[y]
#define mid (l + r >> 1)
#define lson u.lc,l,mid
#define rson u.rc,mid + 1,r
using namespace std;
const int N = 30005;
const int NN = ((1 << 30) - 5);
int tot = 0,n,m,ql,qr,qwl,qwr,K,rt[N];
struct Seg{int lc,rc,cnt;}t[N << 5];
void Ins(int &x,int l,int r,int val,int ct,int y)
{
if(!x)x = ++ tot;
u.cnt = o.cnt;
u.cnt += ct;
if(l == r)return;
if(val <= mid)Ins(lson,val,ct,o.lc),u.rc = o.rc;
else Ins(rson,val,ct,o.rc),u.lc = o.lc;
}
int Qry_val(int x,int l,int r,int y)
{
if(l >= qwl && r <= qwr)
return u.cnt - o.cnt;
int a1 = 0,a2 = 0;
if(qwl <= mid)a1 = Qry_val(lson,o.lc);
if(mid < qwr)a2 = Qry_val(rson,o.rc);
return a1 + a2;
}
int Qry(int x,int l,int r,int y)
{
if(l == r)return l;
int Now = t[u.lc].cnt - t[o.lc].cnt;
if(Now < K)return K -= Now,Qry(rson,o.rc);
else return Qry(lson,o.lc);
}
int main ()
{
freopen("building.in","r",stdin);
freopen("building.out","w",stdout);
scanf("%d",&n);
Rep(i,n)
{
int a,b;
scanf("%d%d",&a,&b);
Ins(rt[i],1,NN,a,b,rt[i - 1]);
}
scanf("%d",&m);
Rep(i,m)
{
scanf("%d%d%d%d%d",&ql,&qr,&qwl,&qwr,&K);
int cpt = Qry_val(rt[qr],1,NN,rt[ql - 1]);
if(cpt < K){puts("-1");continue;}
swap(qwl,qwr),qwl = 1,qwr --;
int cpp;
if(qwl > qwr)cpp = 0;
else cpp = Qry_val(rt[qr],1,NN,rt[ql - 1]);
K = cpp + K;
printf("%d\n",Qry(rt[qr],1,NN,rt[ql - 1]));
}
return 0;
}
T2:
给一个点带权有向图,每个点有花费,你需要选择一些点,使得拆掉这些点之后,不能从入度为0的点到达出度为0的点。
求最小的花费总和。
n<=1000,m<=6000
题解:
炸了。
T3:
给一个数列,数列相当于一条彩带,可以选择把数列的某个位置x切开,拼接a[x,n]和a[1,x - 1]。
求拼接之后的最小字典序的数列。
例子:4 3 2 1
答案:1 4 3 2
题解:
最小表示法。
我们用两个指针去扫,假设:
s[i - > i + k - 1] == s[j - > j + k - 1]
s[i + k] > s[j + k]
则:
考虑i有没有可能是最终答案:
显然不可能对吧,因为从j开始就比i优。
那么i + 1可不可能是最终答案呢?
显然j + 1比i + 1还要优。
所以s[i + k + 1]之后的才有可能作为最终答案。
我们就直接把i跳到i + k + 1的部分就成了。
总的复杂度显然是O(n)的。
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
using namespace std;
int s[300005],n;
int Get(int *a)
{
int i = 1,j = 2,k = 0,len = n;
while(i <= n && j <= n && k <= n)
{
if(k == n)return i;
if(i == j)j ++;
int ni = i + k,nj = j + k;
if(ni > len)ni -= len;
if(nj > len)nj -= len;
if(a[ni] > a[nj])i += k + 1,k = 0;
else if(a[nj] > a[ni])j += k + 1,k = 0;
else k ++;
}
return i;
}
int main ()
{
freopen("work.in","r",stdin);
freopen("work.out","w",stdout);
scanf("%d",&n);
Rep(i,n)scanf("%d",&s[i]);
int pos = Get(s);
for(int i = pos;i <= n;i ++)
printf("%d ",s[i]);
for(int i = 1;i < pos;i ++)
printf("%d ",s[i]);
puts("");
return 0;
}
T3死因:后缀数组写不出来,水不过。