about 7 years ago
Problem Description
Recently George is preparing for the Graduate Record Examinations (GRE for short).
Obviously the most important thing is reciting the words.
Now George is working on a word list containing N words.
He has so poor a memory that it is too hard for him to remember all of the words on the list.
But he does find a way to help him to remember.
He finds that if a sequence of words has a property that for all pairs of neighboring words,
the previous one is a substring of the next one,
then the sequence of words is easy to remember.
So he decides to eliminate some words from the word list first to make the list easier for him.
Meantime, he doesn't want to miss the important words.
He gives each word an importance,
which is represented by an integer ranging from -1000 to 1000,
then he wants to know which words to eliminate to
maximize the sum of the importance of remaining words.
Negative importance just means that George thought
it useless and is a waste of time to recite the word.
Note that although he can eliminate any number of words from the word list,
he can never change the order between words.
In another word,
the order of words appeared on the word list is consistent with the order in the input.
In addition, a word may have different meanings, so it can appear on the list more than once,
and it may have different importance in each occurrence.
Input
The first line contains an integer T(1 <= T <= 50), indicating the number of test cases.
Each test case contains several lines.
The first line contains an integer N(1 <= N <= 2 * 104), indicating the number of words.
Then N lines follows, each contains a string Si and an integer Wi,
representing the word and its importance.
Si contains only lowercase letters.
You can assume that the total length of all words will not exceeded 3 * 105.
Output
For each test case in the input, print one line: "Case #X: Y",
where X is the test case number (starting with 1) and
Y is the largest importance of the remaining sequence of words.
Sample Input
1
5
a 1
ab 2
abb 3
baba 5
abbab 8
Sample Output
Case #1: 14
题目大意
给出一串单词,每个有一个权值。顺序不变的情况下,删掉一些,使得相邻两单词,前一个是后一个的子串。
同时要求使得剩余单词权值和最大。求最大是多少。
题解
AC自动机+fail树+线段树+DP
f[i]表示前i个单词的最大权值和
f[i]=max(f[v]+max(val[i],0)),v是i的子串
我们注意到v是i的子串这个条件还可以表示为v可以是第i个单词的每一个前缀的fail值,然后若我们之间去找v比较麻烦,
反过来想,假设f[i]求出来了,那f[i]会影响所有以它为后缀的字符串(即fail树上它的子树),
然后我们求出fail树的dfs序(f[i]在上面影响一段区间),
所以每次求出f值后的更新就相当于区间更新,查询就是单点查询,这个用线段树维护就好了。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <ctime>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 300005
#define inf 1000000007
typedef long long ll;
priority_queue<int >QwQ;
queue<int >Q;
int num=0,cnt=0,tot,l=0,T,n,ans=0;
int to[INF<<1],h[INF<<1],s[INF],t[INF][27],in[INF],fail[INF],L[INF],R[INF];
int mx[INF*4],A[INF*4],v[INF];
char s1[INF],s2[INF];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=(k<<1)+(k<<3)+ch-'0',ch=getchar();
return k*f;
}
void New(int x)
{
int i,j,k;
rep(i,1,26)t[x][i]=0;
fail[x]=0;
}
void hah(int x,int y)
{
to[++l]=y,h[l]=s[x],s[x]=l;
}
void Insert(int x)
{
int i,j,k,l1,now=1;
scanf("%s",s1);
l1=strlen(s1);
v[x]=read();
rep(i,0,l1-1){
k=s1[i]-'a'+1;
if(!t[now][k])t[now][k]=++tot,New(tot);
now=t[now][k];
s2[++num]=s1[i];
}
in[num]=x;
}
void build_fail()
{
int i,j,k,p;
fail[1]=0,Q.push(1);
while(!Q.empty()){
k=Q.front(),Q.pop();
rep(i,1,26)
if(t[k][i]){
p=fail[k];
while(!t[p][i])p=fail[p];
fail[t[k][i]]=t[p][i];
Q.push(t[k][i]);
}
}
}
void dfs(int x)
{
int i,j,k;
L[x]=++cnt;
for(i=s[x];i;i=h[i])
dfs(to[i]);
R[x]=++cnt;
}
void update(int v)
{
mx[v]=max(mx[v<<1],mx[v<<1|1]);
}
void pushdown(int v)
{
mx[v<<1]=max(mx[v<<1],A[v]);
mx[v<<1|1]=max(mx[v<<1|1],A[v]);
A[v<<1]=max(A[v<<1],A[v]);
A[v<<1|1]=max(A[v<<1|1],A[v]);
A[v]=0;
}
void build(int v,int l,int r)
{
int i,j,k,mid=(l+r)>>1;
mx[v]=A[v]=0;
if(l==r)return ;
build(v<<1,l,mid);
build(v<<1|1,mid+1,r);
update(v);
}
int Query(int v,int l,int r,int x)
{
int i,j,k,mid=(l+r)>>1;
if(l==r)return mx[v];
if(A[v])pushdown(v);
if(x<=mid)return Query(v<<1,l,mid,x);
else return Query(v<<1|1,mid+1,r,x);
update(v);
}
void Modify(int v,int l,int r,int x,int y,int z)
{
int i,j,k,mid=(l+r)>>1;
if(x<=l && r<=y){
mx[v]=max(mx[v],z);
A[v]=max(A[v],z);
return ;
}
if(A[v])pushdown(v);
if(x<=mid)Modify(v<<1,l,mid,x,y,z);
if(y>mid)Modify(v<<1|1,mid+1,r,x,y,z);
update(v);
}
void solve()
{
int i,j,k,mx=0,now=1;
ans=0;
build(1,1,cnt);
rep(i,1,num){
k=s2[i]-'a'+1;
while(!t[now][k])now=fail[now];
now=t[now][k];
mx=max(mx,Query(1,1,cnt,L[now]));
if(in[i]){
k=v[in[i]]>0?v[in[i]]:0;
ans=max(ans,mx+k);
Modify(1,1,cnt,L[now],R[now],mx+k);
mx=0,now=1;
}
}
printf("%d\n",ans);
}
void init()
{
int i,j,k=0;
T=read();
while(T--){
New(1),tot=1,num=0,cnt=0,l=0;
rep(i,1,26)t[0][i]=1;
memset(in,0,sizeof(in));
memset(s,0,sizeof(s));
n=read();
rep(i,1,n)Insert(i);
build_fail();
rep(i,1,tot)hah(fail[i],i);
dfs(0);
printf("Case #%d: ",++k);
solve();
}
}
void work()
{
int i,j,k;
}
int main()
{
init();
work();
return 0;
}
about 7 years ago
题目描述
The best programmers of Embezzland compete to develop
a part of the project called "e-Government"
— the system of automated statistic collecting and press analysis.
We know that any of the k citizens can become a member of the Embezzland government.
The citizens' surnames are a1, a2, ..., ak. All surnames are different.
Initially all k citizens from this list are members of the government.
The system should support the following options:
Include citizen ai to the government.
Exclude citizen ai from the government.
Given a newspaper article text, calculate how politicized it is. To do this,
for every active government member the system counts the number of times his surname occurs
in the text as a substring. All occurrences are taken into consideration,
including the intersecting ones.
The degree of politicization of a text is defined as the sum of these values for
all active government members.
Implement this system.
Input
The first line contains space-separated integers n and k (1 ≤ n, k ≤ 105) —
the number of queries to the system and the number of potential government members.
Next k lines contain the surnames a1, a2, ..., ak, one per line.
All surnames are pairwise different.
Next n lines contain queries to the system, one per line.
Each query consists of a character that determines an operation and the operation argument,
written consecutively without a space.
Operation "include in the government" corresponds to the character "+",
operation "exclude" corresponds to "-".
An argument of those operations is an integer between 1 and k
— the index of the citizen involved in the operation.
Any citizen can be included and excluded from the government
an arbitrary number of times in any order.
Including in the government a citizen who is already there
or excluding the citizen who isn't there changes nothing.
The operation "calculate politicization" corresponds to character "?". Its argument is a text.
All strings — surnames and texts — are non-empty sequences of lowercase Latin letters.
The total length of all surnames doesn't exceed 106,
the total length of all texts doesn't exceed 106.
Output
For any "calculate politicization"
operation print on a separate line the degree of the politicization of
the given text. Print nothing for other operations.
Sample test(s)
Input
7 3
a
aa
ab
?aaab
-2
?aaab
-3
?aaab
+2
?aabbaa
Output
6
4
3
6
题目大意
初始给定n(<=100000)个串,总长不超过1000000,每个串初始时都是白色,
有k个操作,每次操作可以将一个串反色或给定一个串询问在这个串中所有黑色串出现次数之和。询问串总长不超过1000000。
题解
AC自动机+fail树+dfs序+树状数组,其实还是很容易想到的
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <ctime>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 1000005
#define inf 1000000007
typedef long long ll;
priority_queue<int >QwQ;
int tot,top=1,last=0,n,Q,ans=0,l=0,cnt=0;
int t[INF][30],fail[INF],id[INF],col[INF],q[INF],c[INF<<1],to[INF<<1];
int h[INF<<1],s[INF],L[INF],R[INF];
char s1[INF];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=(k<<1)+(k<<3)+ch-'0',ch=getchar();
return k*f;
}
int lowbit(int x){
return x&(-x);
}
void hah(int x,int y)
{
to[++l]=y,h[l]=s[x],s[x]=l;
}
void add(int x,int val)
{
int i,j,k;
for(i=x;i<=cnt;i+=lowbit(i))c[i]+=val;
}
int get_sum(int x)
{
int i,j,k=0;
for(i=x;i;i-=lowbit(i))k+=c[i];
return k;
}
void Insert(int x)
{
int i,j,k,l1,now=1;
scanf("%s",s1);
l1=strlen(s1);
rep(i,0,l1-1){
k=s1[i]-'a'+1;
if(!t[now][k])t[now][k]=++tot;
now=t[now][k];
}
col[x]=1,id[x]=now;
}
void build_fail()
{
int i,j,k,p;
q[0]=1,fail[1]=0;
while(top>last){
k=q[last++];
rep(i,1,26)
if(t[k][i]){
p=fail[k];
while(!t[p][i])p=fail[p];
fail[t[k][i]]=t[p][i];
q[top++]=t[k][i];
}
}
}
void dfs(int x)
{
int i,j,k;
L[x]=++cnt;
for(i=s[x];i;i=h[i])
dfs(to[i]);
R[x]=++cnt;
}
void init()
{
int i,j,k,now,p,x,l1;
tot=1;
rep(i,1,26)t[0][i]=1;
Q=read(),n=read();
rep(i,1,n)Insert(i);
build_fail();
rep(i,1,tot)hah(fail[i],i);
dfs(0);
rep(i,1,n)add(L[id[i]],1),add(R[id[i]]+1,-1);
while(Q--){
scanf("%s",s1);
l1=strlen(s1);
if(s1[0]=='?'){
now=1,ans=0;
rep(i,1,l1-1){
k=s1[i]-'a'+1;
while(!t[now][k])now=fail[now];
now=t[now][k];
ans+=get_sum(L[now]);
}
printf("%d\n",ans);
}
else if(s1[0]=='-'){
sscanf(s1+1,"%d",&x);
if(col[x]){
col[x]=0;
add(L[id[x]],-1),add(R[id[x]]+1,1);
}
}
else{
sscanf(s1+1,"%d",&x);
if(!col[x]){
col[x]=1;
add(L[id[x]],1),add(R[id[x]]+1,-1);
}
}
}
}
void work()
{
int i,j,k;
}
int main()
{
init();
work();
return 0;
}
about 7 years ago
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。
打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母
(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。
打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,
在小键盘上输入两个数(x,y)(其中1≤x,y≤n),
打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,
你能帮助他么?
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
Output
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
Sample Input
aPaPBbP
3
1 2
1 3
2 3
Sample Output
2
1
0
HINT
1<=N<=10^5
1<=M<=10^5
输入总长<=10^5
题解
经典的AC自动机+fail树+dfs序,考试中会有点难想到用dfs序,写起来还是爽的飞起的
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <ctime>
#include <set>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 100005
#define inf 100000007
typedef long long ll;
priority_queue<int >QwQ;
int l1,l=0,m,top=1,last=0,tot=1,cnt=0;
int c[INF<<1],to[INF<<1],h[INF<<1],s[INF<<1],p[INF],fa[INF],t[INF][30],q[INF],fail[INF];
int ans[INF],L[INF],R[INF];
char s1[INF];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=(k<<1)+(k<<3)+ch-'0',ch=getchar();
return k*f;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int v)
{
int i,j,k;
for(i=x;i<=cnt;i+=lowbit(i))c[i]+=v;
}
int getsum(int x)
{
int i,j,k=0;
for(i=x;i;i-=lowbit(i))k+=c[i];
return k;
}
void hah(int x,int y)
{
to[++l]=y,h[l]=s[x],s[x]=l;
}
void Insert()
{
int i,j,k,now=1,id=0;
rep(i,0,l1-1){
if(s1[i]=='P')p[++id]=now;
else if(s1[i]=='B')now=fa[now];
else {
k=s1[i]-'a'+1;
if(!t[now][k])t[now][k]=++tot,fa[tot]=now;
now=t[now][k];
}
}
}
void build_fail()
{
int i,j,k,p;
q[0]=1,fail[1]=0;
while(last<top){
k=q[last++];
rep(i,1,26)
if(t[k][i]){
p=fail[k];
while(!t[p][i])p=fail[p];
fail[t[k][i]]=t[p][i];
q[top++]=t[k][i];
}
}
}
void dfs(int x)
{
int i,j,k;
L[x]=++cnt;
for(i=s[x];i;i=h[i])
dfs(to[i]);
R[x]=++cnt;
}
void solve()
{
int i,j,k,now=1,id=0;
add(L[1],1);
rep(i,0,l1-1){
if(s1[i]=='P'){
id++;
for(j=s[id];j;j=h[j]){
k=p[to[j]];
ans[j]=getsum(R[k])-getsum(L[k]-1);
}
}
else if(s1[i]=='B')add(L[now],-1),now=fa[now];
else now=t[now][s1[i]-'a'+1],add(L[now],1);
}
}
void init()
{
int i,j,k,x,y;
tot=1;
rep(i,1,26)t[0][i]=1;
scanf("%s",s1);
l1=strlen(s1);
Insert();
build_fail();
rep(i,1,tot)hah(fail[i],i);
dfs(0);
l=0;
memset(s,0,sizeof(s));
m=read();
rep(i,1,m){
x=read(),y=read();
hah(y,x);
}
solve();
rep(i,1,m)printf("%d\n",ans[i]);
}
void work()
{
int i,j,k;
}
int main()
{
init();
work();
return 0;
}
over 7 years ago
Problem Description
There are n houses in the village and some bidirectional roads connecting them.
Every day peole always like to ask like this
"How far is it if I want to go from house A to house B"?
Usually it hard to answer. But luckily int this village the answer is always unique,
since the roads are built in the way that there is a unique simple path
("simple" means you can't visit a place twice) between every two houses.
Yout task is to answer all these curious people.
Input
First line is a single integer T(T<=10), indicating the number of test cases.
For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),
the number of houses and the number of queries.
The following n-1 lines each consisting three numbers i,j,k, separated bu a single space,
meaning that there is a road connecting house i and house j,with length k(0<k<=40000).
The houses are labeled from 1 to n.
Next m lines each has distinct integers i and j,
you areato answer the distance between house i and house j.
Output
For each test case,output m lines. Each line represents the answer of the query.
Output a bland line after each test case.
Sample Input
2
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1
Sample Output
10
25
100
100
题解
好久木有写树上倍增了,这段时间一直在复(da)习(bai),因此今晚作死一下,写了一个裸的lca(倍增),差点就忘记怎么写了,
特别是写完后一直以为自己写错了,找了好久错误,然后就交了个“错误代码”,结果QwQ。。。就A了。。。。
其实LCA(倍增)和RMQ差不多,只是LCA是以dfs序为基础的RMQ。。。。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 80010
#define inf 100000007
typedef long long ll;
int n,Q,l=0,tot=0,T;
int to[INF],h[INF],s[INF],sum[INF],t[INF],deep[INF],dist[INF],p[INF],d[INF][30];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=(k<<1)+(k<<3)+ch-'0',ch=getchar();
return k*f;
}
void hah(int x,int y,int z)
{
to[++l]=y,h[l]=s[x],s[x]=l,sum[l]=z;
to[++l]=x,h[l]=s[y],s[y]=l,sum[l]=z;
}
void dfs(int x,int fa)
{
int i,j,k;
t[++tot]=x;
p[x]=tot;
for(i=s[x];i;i=h[i])
if(to[i]==fa)continue;
else {
deep[to[i]]=deep[x]+1;
dist[to[i]]=dist[x]+sum[i];
dfs(to[i],x);
t[++tot]=x;
}
}
void RMQ()
{
int i,j,k;
rep(i,0,tot)d[i][0]=t[i];
for(j=1;(1<<j)<tot;j++)
for(i=0;i+(1<<j)-1<tot;i++)
if(deep[d[i][j-1]]<deep[d[i+(1<<(j-1))][j-1]])
d[i][j]=d[i][j-1];
else d[i][j]=d[i+(1<<(j-1))][j-1];
}
int lca(int x,int y)
{
int i,j,k=0;
x=p[x],y=p[y];
if(x>y)swap(x,y);
while((1<<k)<=y-x+1)k++;
k--;
return deep[d[x][k]]<deep[d[y-(1<<k)+1][k]]?d[x][k]:d[y-(1<<k)+1][k];
}
void init()
{
int i,j,k,x,y,z;
T=read();
while(T--){
n=read(),Q=read();
l=0,tot=-1;
rep(i,1,n-1){
x=read(),y=read(),z=read();
hah(x,y,z);
}
rep(i,1,n)dist[i]=deep[i]=p[i]=t[i]=0;
dfs(1,0);
RMQ();
//rep(i,1,n)cout<<p[i]<<endl;
rep(i,1,Q){
x=read(),y=read();
cout<<dist[x]+dist[y]-2*dist[lca(x,y)]<<endl;
//cout<<lca(x,y)<<endl;
}
memset(s,0,sizeof(s));
}
}
void work()
{
int i,j,k;
}
int main()
{
init();
work();
return 0;
}
over 7 years ago
Description
You are working for Macrohard company in data structures department.
After failing your previous task about key insertion you were asked to write a new data structure
that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1...n] of different integer numbers,
your program must answer a series of questions Q(i, j, k)
in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?"
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3).
The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6),
the third number is 5, and therefore the answer to the question is 5.
Input
The first line of the input file contains n --- the size of the array,
and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 10 9
by their absolute values --- the array for which the answers should be given.
The following m lines contain question descriptions,
each description consists of three numbers: i, j,
and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).
Output
For each question output the answer to it --- the k-th number in sorted a[i...j] segment.
Sample Input
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
Sample Output
5
6
3
Hint
This problem has huge input,so please use c-style input(scanf,printf),
or you may got time limit exceed.
题解
题目大意:维护一个序列,求静态的区间第k大
静态的区间第k大,主席树。。。。貌似上一篇是动态区间第k大,用的是树套树,貌似这里应该也可以用树套树吧。。。
QwQ,写的话多一个logn,我才没无聊到那种程度呢。。。。。TAT
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <ctime>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 2000005
#define inf 1000000007
typedef long long ll;
int n,m,mx=0,tot=0,cnt=0;
int a[INF],L[INF],R[INF],root[INF],sum[INF],d[INF];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=k*10+ch-'0',ch=getchar();
return k*f;
}
void build_tree(int l,int r,int x,int &y,int v)
{
int i,j,k,mid=(l+r)>>1;
y=++tot;
sum[y]=sum[x]+1;
if(l==r)return;
L[y]=L[x],R[y]=R[x];
if(v<=mid)build_tree(l,mid,L[x],L[y],v);
else build_tree(mid+1,r,R[x],R[y],v);
}
int query(int x,int y,int z)
{
int i,j,k,l=1,r=cnt,mid;
x=root[x-1],y=root[y];
while(l<r){
mid=(l+r)>>1;
if(sum[L[y]]-sum[L[x]]>=z)x=L[x],y=L[y],r=mid;
else z-=sum[L[y]]-sum[L[x]],x=R[x],y=R[y],l=mid+1;
}
return l;
}
void init()
{
int i,j,k;
n=read(),m=read();
rep(i,1,n)a[i]=read(),d[++cnt]=a[i];
sort(d+1,d+cnt+1);
cnt=unique(d+1,d+cnt+1)-d-1;
rep(i,1,n)a[i]=lower_bound(d+1,d+cnt+1,a[i])-d;
rep(i,1,n)build_tree(1,cnt,root[i-1],root[i],a[i]);
}
void work()
{
int i,j,k,x,y,z;
while(m--){
x=read(),y=read(),z=read();
printf("%d\n",d[query(x,y,z)]);
}
}
int main()
{
init();
work();
return 0;
}
over 7 years ago
Description
The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied
with the query like to simply find the k-th smallest number of the given N numbers.
They have developed a more powerful system such that for N numbers a[1], a[2], ..., a[N],
you can ask it like: what is the k-th smallest number of a[i], a[i+1], ..., a[j]?
(For some i<=j, 0<k<=j+1-i that you have given to it).
More powerful, you can even change the value of some a[i], and continue to query, all the same.
Your task is to write a program for this computer, which
- Reads N numbers from the input (1 <= N <= 50,000)
- Processes M instructions of the input (1 <= M <= 10,000).
These instructions include querying the k-th smallest number of a[i], a[i+1], ..., a[j]
and change some a[i] to t.
Input
The first line of the input is a single number X(0<X<=4),the number of the test cases of the input.
Then X blocks each represent a single test case.
The first line of each block contains two integers N and M, representing N numbers and M instruction.
It is followed by N lines. The (i+1)-th line represents the number a[i].
Then M lines that is in the following format
Q i j k or
C i t
It represents to query the k-th number of a[i], a[i+1], ..., a[j] and change some a[i] to t,
respectively.
It is guaranteed that at any time of the operation.
Any number a[i] is a non-negative integer that is less than 1,000,000,000.
There're NO breakline between two continuous test cases.
Output
For each querying operation, output one integer to represent the result.
(i.e. the k-th smallest number of a[i], a[i+1],..., a[j])
There're NO breakline between two continuous test cases.
Sample Input
2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
Sample Output
3
6
3
6
题解
题目大意:T组询问,每组给你一个序列,然后每次会修改一个值,或者询问区间第k大值,然后你回答就是的啦。。。
裸的树套树,我又写了一次线段树套平衡树,比起二逼平衡树容易多了,貌似还可以用主席树套树状数组,
等一下我再来用这个写一遍。。。。。QwQ
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <ctime>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 1000005
#define inf 1000000007
typedef long long ll;
int n,m,tot=0,op=0,num=0,T;
int v[INF],size[INF],same[INF],L[INF],R[INF],weight[INF],root[200005],a[50005];
char s[5];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=k*10+ch-'0',ch=getchar();
return k*f;
}
void update(int x)
{
size[x]=size[L[x]]+size[R[x]]+same[x];
}
void updown_l(int &x)
{
int l=L[x],r=R[l];
L[x]=R[l],R[l]=x,update(x),update(l),x=l;
}
void updown_r(int &x)
{
int r=R[x],l=L[r];
R[x]=L[r],L[r]=x,update(x),update(r),x=r;
}
void Insert(int &x,int y)
{
int i,j,k;
if(!x){
x=++tot;
size[x]=same[x]=1;
weight[x]=rand();
v[x]=y;
return;
}
size[x]++;
if(y==v[x]){
same[x]++;
return;
}
if(y<v[x]){
Insert(L[x],y);
if(weight[x]>weight[L[x]])updown_l(x);
}
else {
Insert(R[x],y);
if(weight[x]>weight[R[x]])updown_r(x);
}
}
void Delet(int &x,int y)
{
int i,j,k;
if(!x)return;
if(v[x]==y){
if(same[x]>1){
same[x]--,size[x]--;
return;
}
else if(!L[x] || !R[x]){x=L[x]+R[x];return;}
else if(weight[L[x]]<weight[R[x]])updown_l(x);
else updown_r(x);
Delet(x,y);
return;
}
size[x]--;
if(v[x]<y)Delet(R[x],y);
else Delet(L[x],y);
}
void find_rank(int x,int y)
{
int i,j,k;
if(!x)return;
if(v[x]==y){
num+=size[L[x]],op+=same[x];
return;
}
if(v[x]<y){
num+=size[L[x]]+same[x];
find_rank(R[x],y);
}
else find_rank(L[x],y);
}
void build_tree(int v,int l,int r,int id,int x)
{
int i,j,k,mid=(l+r)>>1;
Insert(root[v],x);
if(l==r)return;
if(id<=mid)build_tree(v<<1,l,mid,id,x);
else build_tree(v<<1|1,mid+1,r,id,x);
}
void find_rank_QwQ(int v,int l,int r,int x,int y,int z)
{
int i,j,k,mid=(l+r)>>1;
if(x<=l && r<=y){
find_rank(root[v],z);
return;
}
if(x<=mid)find_rank_QwQ(v<<1,l,mid,x,y,z);
if(y>mid)find_rank_QwQ(v<<1|1,mid+1,r,x,y,z);
}
int find_num_QwQ(int x,int y,int z)
{
int i,j,k,mid,l=0,r=1e+9;
while(l<r){
mid=(l+r)>>1,num=0,op=0;
find_rank_QwQ(1,1,n,x,y,mid);
if(num+op<z)l=mid+1;
else r=mid;
}
return l;
}
void Modify(int v,int l,int r,int id,int x)
{
int i,j,k,mid=(l+r)>>1;
Delet(root[v],a[id]);
Insert(root[v],x);
if(l==r)return;
if(id<=mid)Modify(v<<1,l,mid,id,x);
else Modify(v<<1|1,mid+1,r,id,x);
}
void work()
{
int i,j,k,x,y,z;
while(m--){
scanf("%s",s);
if(s[0]=='Q'){
x=read(),y=read(),z=read();
printf("%d\n",find_num_QwQ(x,y,z));
}
else {
x=read(),y=read();
Modify(1,1,n,x,y),a[x]=y;
}
}
}
void init()
{
int i,j,k;
T=read();
while(T--){
tot=0;
memset(root,0,sizeof(root));
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
n=read(),m=read();
rep(i,1,n)
a[i]=read(),build_tree(1,1,n,i,a[i]);
work();
}
}
int main()
{
init();
return 0;
}
over 7 years ago
Description
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
Input
第一行两个数 n,m 表示长度为n的有序序列和m个操作
第二行有n个数,表示有序序列
下面有m行,opt表示操作标号
若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继
Output
对于操作1,2,4,5各输出一行,表示查询结果
Sample Input
9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5
Sample Output
2
4
3
4
9
HINT
1.n和m的数据范围:n,m<=50000
2.序列中每个数的数据范围:[0,1e8]
3.虽然原题没有,但事实上5操作的k可能为负数
题解
这道题的原来是treap,原题叫普通平衡树,我写的treap(反正blog里面有);改变题目后要求维护区间的一些操作,
然而维护区间操作自然想到线段树,但是它的有些操作线段树不可做,需要用平衡树,因此就是线段树套平衡树啦。
一开始我想平衡树套线段树,但后面一些有操作貌似不可做,因此就改写线段树套平衡树。。。。。(人弱没办法)
线段树维护序列的区间,然后对于每一个区间建一颗平衡树,然后区间第k大,由于treap没法像值域线段树那样直接求出答案,
因此我们二分答案再在区间上判断它的排名,其它的操作自己都可以YY出来的。。。。。。
然后这道题我改了很久,因为我是二逼,第一个是把操作二的输入看反了,第二是在二分上纠结了很久,
其实是那时忘记了有相同的数,然后我就干脆算出相同数中最大的位置,然后就可以二分了。。。。简直是日了poi
其实我这次快读是写的最蛋疼的一次。。。。。200行的代码,感觉棒棒的,TAT
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <ctime>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 2000005
#define inf 1000000007
typedef long long ll;
int n,tot=0,num=0,ans=0,m,op=0;
int size[INF],same[INF],L[INF],R[INF],v[INF],weight[INF],root[INF],a[INF];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=(k<<1)+(k<<3)+ch-'0',ch=getchar();
return k*f;
}
void update(int x)
{
int l=L[x],r=R[x];
size[x]=size[l]+size[r]+same[x];
}
void updown_l(int &x)
{
int l=L[x],r=R[l];
L[x]=R[l],R[l]=x,update(x),update(l),x=l;
}
void updown_r(int &x)
{
int r=R[x],l=L[r];
R[x]=L[r],L[r]=x,update(x),update(r),x=r;
}
void Insert(int &x,int y)
{
int i,j,k;
if(!x){
x=++tot;
size[x]=same[x]=1,v[x]=y;
weight[x]=rand();
return;
}
size[x]++;
if(y==v[x]){
same[x]++;
return;
}
if(y<v[x]){
Insert(L[x],y);
if(weight[x]>weight[L[x]])updown_l(x);
}
else {
Insert(R[x],y);
if(weight[x]>weight[R[x]])updown_r(x);
}
}
void Delet(int &x,int y)
{
int i,j,k;
if(!x)return;
if(v[x]==y){
if(same[x]>1){
same[x]--,size[x]--;
return;
}
if(!L[x] || !R[x]){x=L[x]+R[x];return;}
else if(weight[L[x]]<=weight[R[x]])updown_l(x);
else updown_r(x);
Delet(x,y);
return;
}
size[x]--;
if(y<v[x])Delet(L[x],y);
else Delet(R[x],y);
}
void find_rank(int x,int y)
{
int i,j,k;
if(!x)return;
if(v[x]==y){
num+=size[L[x]],op+=same[x];
return;
}
if(y<v[x])find_rank(L[x],y);
else {
num+=size[L[x]]+same[x];
find_rank(R[x],y);
return;
}
}
int find_num(int x,int id)
{
int i,j,k;
if(!x)return 0;
if(id<=size[L[x]])return find_num(L[x],id);
else if(id<=size[L[x]]+same[x])return v[x];
else return find_num(R[x],id-size[L[x]]-same[x]);
}
void Precursor(int x,int y)
{
int i,j,k;
if(!x)return;
if(y<=v[x])Precursor(L[x],y);
else ans=max(ans,v[x]),Precursor(R[x],y);
}
void Follow(int x,int y)
{
int i,j,k;
if(!x)return;
if(y<v[x])ans=min(ans,v[x]),Follow(L[x],y);
else Follow(R[x],y);
}
void change(int v,int l,int r,int id,int x)
{
int i,j,k,mid=(l+r)>>1;
Insert(root[v],x);
if(l==r)return;
if(id<=mid)change(v*2,l,mid,id,x);
else change(v*2+1,mid+1,r,id,x);
}
void find_rank_QwQ(int v,int l,int r,int x,int y,int z)
{
int i,j,k,mid=(l+r)>>1;
if(x<=l && r<=y){
find_rank(root[v],z);
return;
}
if(x<=mid)find_rank_QwQ(v*2,l,mid,x,y,z);
if(y>mid)find_rank_QwQ(v*2+1,mid+1,r,x,y,z);
}
int find_num_QwQ(int x,int y,int z)
{
int i,j,k=0,l=0,r=1e+8,mid;
while(l<r){
mid=(l+r)>>1,num=0,op=0;
find_rank_QwQ(1,1,n,x,y,mid);
if(num+op<z)l=mid+1;//有相同的数,所以我干脆算出相同数中最大的位置
else r=mid;
}
return r;
}
void modify(int v,int l,int r,int id,int x)
{
int i,j,k,mid=(l+r)>>1;
Delet(root[v],a[id]);
Insert(root[v],x);
if(l==r)return;
if(id<=mid)modify(v*2,l,mid,id,x);
else modify(v*2+1,mid+1,r,id,x);
}
void Precursor_QwQ(int v,int l,int r,int x,int y,int z)
{
int i,j,k,mid=(l+r)>>1;
if(x<=l && r<=y){
Precursor(root[v],z);
return;
}
if(x<=mid)Precursor_QwQ(v*2,l,mid,x,y,z);
if(y>mid)Precursor_QwQ(v*2+1,mid+1,r,x,y,z);
}
void Follow_QwQ(int v,int l,int r,int x,int y,int z)
{
int i,j,k,mid=(l+r)>>1;
if(x<=l && r<=y){
Follow(root[v],z);
return;
}
if(x<=mid)Follow_QwQ(v*2,l,mid,x,y,z);
if(y>mid)Follow_QwQ(v*2+1,mid+1,r,x,y,z);
}
void init()
{
int i,j,k,x,y,z;
n=read(),m=read();
rep(i,1,n)a[i]=read(),change(1,1,n,i,a[i]);
}
void work()
{
int i,j,k,x,y,z;
while(m--){
k=read();
if(k==1)x=read(),y=read(),z=read(),op=0,num=1,find_rank_QwQ(1,1,n,x,y,z),printf("%d\n",num);
else if(k==2)x=read(),y=read(),z=read(),printf("%d\n",find_num_QwQ(x,y,z));
else if(k==3)x=read(),y=read(),modify(1,1,n,x,y),a[x]=y;
else if(k==4)x=read(),y=read(),z=read(),ans=0,Precursor_QwQ(1,1,n,x,y,z),printf("%d\n",ans);
else x=read(),y=read(),z=read(),ans=inf,Follow_QwQ(1,1,n,x,y,z),printf("%d\n",ans);
}
}
int main()
{
init();
work();
return 0;
}
over 7 years ago
Description
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
Input
第一行N,M
接下来M行,每行形如1 a b c或2 a b c
Output
输出每个询问的结果
Sample Input
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
1
2
1
HINT
【样例说明】
第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1
的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是
1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3
大的数是 1 。
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中abs(c)<=Maxlongint
题解
貌似这个就是你们口中的线段树套线段树,我写的是外面是权值线段树,
然后对于这个线段树的每一个区间维护一个当前时刻的区间(1~n)线段树,
表示小于等于这个维护值域的区间中任何一个值的数在区间线段树某个区间中的出现次数,
然后就是和普通线段树一样打标记(下传),查询就是二分(其实就是在值域线段树上二分),
看左右子树在这个区间上的出现次数,然后左右移,就好了。。。。QwQ
注意一点:那个我们在值域线段树上二分每次只能求出第k小值,题目要求第k大,因此我们把每次插入的数反过来,
即让这个数-n-1,然后答案就加上n+1就好了。。。。。QwQ
然后其他东西都可以自己YY出来,感觉这道题就是看脑补能力。。。。TAT
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 20000005
#define inf 100000007
typedef long long ll;
int n,m,tot=0;
int b[INF],sum[INF],L[INF],R[INF],root[200005];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=k*10+ch-'0',ch=getchar();
return k*f;
}
void update(int v)
{
sum[v]=sum[L[v]]+sum[R[v]];
}
void pushdown(int v,int x)
{
b[L[v]]+=b[v];
b[R[v]]+=b[v];
sum[L[v]]+=(x+1)/2*b[v];
sum[R[v]]+=(x-(x+1)/2)*b[v];
b[v]=0;
}
void build_tree(int l,int r,int x,int y,int &p)
{
int i,j,k,mid=(l+r)>>1;
if(!p)p=++tot;
if(x<=l && r<=y){
b[p]++;
sum[p]+=r-l+1;
return;
}
if(!L[p])L[p]=++tot;
if(!R[p])R[p]=++tot;
if(b[p])pushdown(p,r-l+1);
if(x<=mid)build_tree(l,mid,x,y,L[p]);
if(y>mid)build_tree(mid+1,r,x,y,R[p]);
update(p);
}
void Insert(int x,int y,int z)//值域线段树区间从1~n
{
int i,j,k,mid,l=1,r=n,t=1;
while(l<r){
mid=(l+r)>>1;
build_tree(1,n,x,y,root[t]);
if(z<=mid){
t=t<<1;
r=mid;
}
else t=t<<1|1,l=mid+1;
}
build_tree(1,n,x,y,root[t]);
}
int query(int l,int r,int x,int y,int &p)
{
int i,j,k=0,mid=(l+r)>>1,q=0;
if(!p)return 0;
if(x<=l && r<=y){
return sum[p];
}
if(!L[p])L[p]=++tot;
if(!R[p])R[p]=++tot;
if(b[p])pushdown(p,r-l+1);
if(x<=mid)k=query(l,mid,x,y,L[p]);
if(y>mid)q=query(mid+1,r,x,y,R[p]);
return k+q;
}
int find_ans(int x,int y,int z)
{
int i,j,k,l=1,r=n,mid,t=1;
while(l<r){
mid=(l+r)>>1;
k=query(1,n,x,y,root[(t<<1)]);
if(k<z){
z-=k;
t=t<<1|1;
l=mid+1;
}
else t=t<<1,r=mid;
}
l=-l+n+1;//+1是为了反过来的时候防止变成0(因为二分的时候从1开始)
return l;
}
void init()
{
int i,j,k,t,x,y,z;
n=read(),m=read();
while(m--){
t=read(),x=read(),y=read(),z=read();
if(t==1){
z=-z+n+1;//因为求区间第k大值就是把所有的数反过来的区间第k小值
Insert(x,y,z);//反过来就是对于一个数x变成x-n-1
}
else printf("%d\n",find_ans(x,y,z));
}
}
void work()
{
int i,j,k;
}
int main()
{
init();
work();
return 0;
}
over 7 years ago
Description
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,
你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
Input
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。
以下m行每行一个正整数,依次为每次删除的元素。
Output
输出包含m行,依次为删除每个元素之前,逆序对的个数。
Sample Input
5 4
1
5
3
4
2
5
1
4
2
Sample Output
5
2
2
1
样例解释
(1,5,3,4,2) (1,3,4,2) (3,4,2) (3,2) (3)。
HINT
N<=100000 M<=50000
题解
看来我要开始口胡了,QwQ。。。。其实这道题我考过,以为可以用treap做,表示当时不会树套树。。。。
首先对于一般的静态逆序对,可以用树状数组求出这个数前面有多少个数比它大,后面有多少个数比它小(我有一篇博客中提到了)
然后我们每次删除x答案就要减去跟它相关的逆序对再加上被删除的数中跟它相关的逆序对,前面的那个随便搞,
至于后面的那个我们用线段树维护树状数组的每一个节点,即第i颗线段树维护(i-lowbit(i)+1)~i的区间,树状数组表示下标
然后每次删除x就更新那些与x相关的线段树,其实就是动态维护这个时刻被已经被删除的数的树状数组,
然后查询就是跟一般的主席树查询差不多,但是分两次查询,先查询位置在x之前比它大的被删除的数的个数,
然后就是后面比它小的数的个数,然后就是跟以前一样在l-1和r这两颗线段树上左右移,
只不过这里就是最多logn个线段树一起左右移动,因为更l-1有关的区间(线段树)最多有logn个,r同理。。。。QwQ
然后就是一些细节了,还是%代码吧。。。。QwQ
复杂度:O(nlogn^2).
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <ctime>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 400005
#define inf 1000000007
typedef long long ll;
int n,m,top=0,last=0,tot=0;
ll ans=0;
int R[5000005],L[5000005],c[INF],a[INF],num[INF],t[INF],d[INF];
int sum[5000005],pre[INF],out[INF],root[INF];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=k*10+ch-'0',ch=getchar();
return k*f;
}
int lowbit(int x)
{
return x&(-x);
}
void build_tree(int l,int r,int &y,int v)//第i颗线段树维护区间(i-lowbit(i)+1)~i中的数出现次数总和
{
int i,j,k,mid=(l+r)>>1;
if(!y)y=++tot;
sum[y]++;
if(l==r)return;
if(v<=mid)build_tree(l,mid,L[y],v);
else build_tree(mid+1,r,R[y],v);
}
void add(int x)
{
int i,j,k;
for(i=x;i<=n;i+=lowbit(i))c[i]++;
}
int get_sum(int x)//get_sum(x)可以算出现在比x小的数的个数
{
int i,j,k;
int sum=0;
for(i=x;i;i-=lowbit(i))sum+=c[i];
return sum;
}
void init()
{
int i,j,k;
n=read(),m=read();
rep(i,1,n){
a[i]=read();
num[a[i]]=i;
add(a[i]);
pre[i]=i-get_sum(a[i]);
ans+=pre[i];//pre[i]表示下标为i的数的之前有多少个比它大的数
}
memset(c,0,sizeof(c));
ser(i,n,1){
out[i]=get_sum(a[i]-1);//out[i]表示之后又多少个比它小的数
add(a[i]);
}
//rep(i,1,n)cout<<pre[i]<<' '<<out[i]<<endl;
}
int query_pre(int l,int r,int x)//计算出在x被删除之前的被删除序列中有多少个数比x大且位置在x之前
{
int i,j,k,mid,tmp=0;
top=last=0;
for(j=r;j;j-=lowbit(j))t[++last]=root[j];
l=1,r=n;
while(l<r){
mid=(l+r)>>1;
if(x<=mid){
rep(i,1,last)tmp+=sum[R[t[i]]],t[i]=L[t[i]];
r=mid;
}
else {
rep(i,1,last)t[i]=R[t[i]];
l=mid+1;
}
}
return tmp;
}
int query_out(int l,int r,int x)//计算出在x被删除之前的被删除序列中有多少个数比x小且位置在x之后
{
int i,j,k,mid,tmp=0;
top=last=0;
for(i=l-1;i;i-=lowbit(i))t[++top]=root[i];//每次我们要记录最多log(n)颗树,然后左右移动
for(j=r;j;j-=lowbit(j))d[++last]=root[j];
l=1,r=n;
while(l<r){
mid=(l+r)>>1;
if(x>mid){//由于x>mid,所以这log(n)颗树的左子树都是答案的一部分
rep(j,1,last)tmp+=sum[L[d[j]]],d[j]=R[d[j]];
rep(i,1,top)tmp-=sum[L[t[i]]],t[i]=R[t[i]];//减去多算的在x之前的比它大的数的个数
l=mid+1;
}
else {
rep(i,1,top)t[i]=L[t[i]];
rep(j,1,last)d[j]=L[d[j]];
r=mid;
}
}
return tmp;
}
void work()
{
int i,j,k,x,y;
rep(i,1,m){
printf("%lld\n",ans);
x=read(),y=num[x];
ans=ans-pre[y]-out[y]+query_pre(1,y,x)+query_out(y+1,n,x);
for(j=y;j<=n;j+=lowbit(j))build_tree(1,n,root[j],x);
//每次删除一个数就对于所有影响到它的树状数组的节点修改(或者说建树)
}//root[j]和root[j-1]没有半毛钱关系,他们维护着不同的区间
}
int main()
{
init();
work();
return 0;
}
over 7 years ago
今天上午考试一道费用流的SB题,竟然没做出来(其实我是一直在做以为是线段树的题目,题解是块状链表的QwQ)
题目大意:
一个n * m的矩阵,每个位置的价值为w[i][j],然后要求每行最多不能选超过两个,每列做多不能选超过两个的最大价值。
题解
首先建一个源S和汇T,要对行和列限流,与格子无关(每个位置只能被选一次),因此我们拆成n个点和m个点,S向n个点连边,
流量为2,费用为0,m个点向T连边,流量为2,费用为0,然后每个格子w[i][j],第i个点向第j+n个点连边流量为1,
费用为-w[i][j],就是把每个格子拆成两个点,然后限流,因为我们要求的是最大费用,所以我们应把所有的费用先变相反数
,然后答案就是跑出来的答案的相反数,注意当SPFA的费用大于0时要退出,不然答案会减小,因为费用流会考虑所以情况
感觉zkw费用流并不可以跑吧。。。。。QwQ
SPFA最大费用最大流
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define inf 10000007
#define INF 1000005
queue<int >Q;
int n,m,l=1,ans1=0,ans2=0,T=0;
int to[INF],h[INF],s[INF],sum[INF],vim[INF],of[INF],w[2000][2000],in[INF],dist[INF],pre[INF];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=k*10+ch-'0',ch=getchar();
return k*f;
}
void hah(int x,int y,int z,int v)
{
to[++l]=y,h[l]=s[x],s[x]=l,sum[l]=z,vim[l]=v,of[l]=x;
to[++l]=x,h[l]=s[y],s[y]=l,sum[l]=0,vim[l]=-v,of[l]=y;
}
void init()
{
int i,j,k;
n=read(),m=read();
T=n+m+1;
rep(i,1,n)
rep(j,1,m){
w[i][j]=read();
hah(i,j+n,1,-w[i][j]);//对于每个格子只能选择一次,我们要求的是最大费用,所以改为负
}
rep(i,1,n)hah(0,i,2,0);//对于每行限流为2
rep(i,1,m)hah(n+i,T,2,0);//对于每列限流为2
}
int spfa()
{
int i,j,k;
dist[0]=0;
rep(i,1,T)dist[i]=inf;
Q.push(0),in[0]=1;
while(!Q.empty()){
k=Q.front(),Q.pop();
for(i=s[k];i;i=h[i])
if(sum[i] && dist[to[i]]>dist[k]+vim[i]){
dist[to[i]]=dist[k]+vim[i];
if(!in[to[i]])Q.push(to[i]),in[to[i]]=1;
pre[to[i]]=i;
}
in[k]=0;
}
if(dist[T]!=inf && dist[T]<0)return 1;//当dist[T]大于0时,答案会减少,因为每次的流量是大于0的 QwQ
return 0;//ans2-=dist[T]*k;k表示流量
}
void dinic()
{
int i,j,k,x=inf;
j=ans2;
ans1++;
k=pre[T];
while(k){
x=min(x,sum[k]);
k=pre[of[k]];
}
k=pre[T];
while(k){
sum[k]-=x;
sum[k^1]+=x;
ans2+=x*vim[k];
k=pre[of[k]];
}
//cout<<ans2<<endl;
}
void work()
{
int i,j,k;
while(spfa())dinic();
ans2=-ans2;
cout<<ans2<<endl;
}
int main()
{
freopen("pick.in","r",stdin);
freopen("pick.out","w",stdout);
init();
work();
fclose(stdin);
fclose(stdout);
return 0;
}