Zju2112 Dynamic Rankings

Zju2112 Dynamic Rankings

Time Limit:10000MS  Memory Limit:165536K
Total Submit:18 Accepted:7
Case Time Limit:1000MS

Description

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在 a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对 改变后的a继续回答上面的问题。
你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。对于每一个询问指令,你必须输出正确的回答。

第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。
第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。
接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。
Q i j k 或者 C i t
Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。

Input

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

Output

Sample Input

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6

Hint

20%的数据中,m,n≤100;
40%的数据中,m,n≤1000;
100%的数据中,m,n≤10000。

Source

各位神牛至少3KB的代码量让我很冏。。一般来说是要树套树的把,但我感觉那样很难写,索性写个块状数组算了。。结果发现速度非常快冏。。。
对每个块保存排序的结果,同时保存整体所有的数,那么查询一个数在一个区间中比它小的数数量是
O(N^0.5LogN)的。。再加上二分找这个数,就是N^0.5*LogN^2了。。
还有修改的话直接修改一排序的结果(插入排序)和整体这个数,是O(N^0.5)的。。但我感觉查询都那么慢了修改也无所谓了,就暴力sort了冏。。。由于这里不能搞出当前所有数的排列,所以二分只能按照Log(10^9)=30来搞,实际上如果先离线一下读入所有数就可以直接在所有数中二分,那么就是Log(20000)=15..快一倍。。不过没必要。。。
Code:
#include <vector>
#include <algorithm>
#include <utility>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#define rep(i,n) for(int i=0;i<n;i++)
#define For(i,l,r) for(int i=l;i<=r;i++)
#define pb push_back
const int inf=~0U>>1,maxn=10000,size=100;
int T[size][size],N[size]={},A[maxn],n,m,t,d=1<<6;
using namespace std;
void init()
{
scanf("%d%d",&n,&m);rep(i,n)scanf("%d",A+i),T[i/size][N[i/size]++]=A[i];
t=1+(n-1)/size;rep(i,t)sort(T[i],T[i]+N[i]);
}
int get(int*A,int s,int x)
{
int i,l;
for(i=s,l=d;l;l>>=1)
if(i-l>=0&&A[i-l]>=x)i-=l;
return i;
}
int Count(int l,int r,int x)
{
int a=l/size,b=r/size,s=0;;
if(a==b){For(i,l,r)s+=A[i]<x;return s;}
For(i,l,(a+1)*size-1)s+=A[i]<x;
For(i,b*size,r)s+=A[i]<x;a++;b–;
For(i,a,b)s+=get(T[i],N[i],x);
return s;
}
int kth(int l,int r,int k)
{
#define C(x) (Count(l,r,x))
int i,d;
for(i=0,d=1<<30;d;d>>=1)
if(C(i+d)<k)i+=d;
return i;
}
void Change(int p,int x)
{
int a=p/size;int t=A[p];A[p]=x;
t=lower_bound(T[a],T[a]+N[a],t)-T[a];
T[a][t]=x;sort(T[a],T[a]+N[a]);
}
int main()
{
//freopen("in","r",stdin);
init();char c;int i,j,k;
while(m–)
{
scanf("n%c",&c);
if(c==’C’)scanf("%d%d",&i,&j),Change(i-1,j);
else scanf("%d%d%d",&i,&j,&k),printf("%dn",kth(i-1,j-1,k));
}
}

[Sdoi2009]Elaxia的路线

[Sdoi2009]Elaxia的路线

Time Limit:4000MS  Memory Limit:65536K
Total Submit:3 Accepted:2
Case Time Limit:1000MS

Description

最近,Elaxia和w**的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们
必须合理地安排两个人在一起的时间。Elaxia和w**每天都要奔波于宿舍和实验室之间,他们
希望在节约时间的前提下,一起走的时间尽可能的长。
现在已知的是Elaxia和w**所在的宿舍和实验室的编号以及学校的地图:地图上有N个路
口,M条路,经过每条路都需要一定的时间。
具体地说,就是要求无向图中,两对点间最短路的最长公共路径。

Input

第一行:两个整数N和M(含义如题目描述)。
第二行:四个整数x1、y1、x2、y2(1 ≤ x1 ≤ N,1 ≤ y1 ≤ N,1 ≤ x2 ≤ N,1 ≤
≤ N),分别表示Elaxia的宿舍和实验室及w**的宿舍和实验室的标号(两对点分别
x1,y1和x2,y2)。
接下来M行:每行三个整数,u、v、l(1 ≤ u ≤ N,1 ≤ v ≤ N,1 ≤ l ≤ 10000),表
u和v之间有一条路,经过这条路所需要的时间为l。
出出出格格格式式式:::
一行,一个整数,表示每天两人在一起的时间(即最长公共路径的长度)。

Output

一行,一个整数,表示每天两人在一起的时间(即最长公共路径的长度)

Sample Input

9 10
1 6 7 8
1 2 1
2 5 2
2 3 3
3 4 2
3 9 5
4 5 3
4 6 4
4 7 2
5 8 1
7 9 1

Sample Output

3

Hint

对于30%的数据,N ≤ 100;
对于60%的数据,N ≤ 1000;
对于100%的数据,N ≤ 1500,输入数据保证没有重边和自环。

Source

Day2
额。。看上去很吓人,实际上还是蛮水的。。
首先可以注意到公共的路径必然是一段。。然后找出所有可以同时出现在两个最短路的边,用Dp求出最长的链就OK了。。。
Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define rep(i,n) for(int i=0;i<n;i++)
const int inf=~0U>>1,maxn=1500;
using namespace std;
int n,m,S[2],T[2];
struct Edge
{
int t,c;Edge*next;
Edge(int _t,int _c,Edge*_n):t(_t),c(_c),next(_n){}
}*E[maxn]={};
void AddEdge(int s,int t,int c)
{
E[s]=new Edge(t,c,E[s]);
E[t]=new Edge(s,c,E[t]);
}
int DistS[2][maxn],DistT[2][maxn];
struct Queue
{
int Q[maxn],h,t;bool inQ[maxn];
Queue(){h=t=0;memset(inQ,0,sizeof(inQ));}
void inc(int&i){if(++i==maxn)i=0;}
void put(int x)
{
if(inQ[x])return;inQ[x]=true;
Q[t]=x;inc(t);
}
int get()
{
int tmp=Q[h];inc(h);inQ[tmp]=false;
return tmp;
}
bool empty(){return h==t;}
};
void Spfa(int vs,int Dist[maxn])
{
static Queue Q;
rep(i,n)Dist[i]=inf;Dist[vs]=0;Q.put(vs);
while(!Q.empty())
{
int t=Q.get(),c=Dist[t];
for(Edge*e=E[t];e;e=e->next)
if(c+e->c<Dist[e->t])
Dist[e->t]=c+e->c,Q.put(e->t);
}
}
int D[2];
bool inPath(int v)
{
rep(i,2)if(DistS[i][v]+DistT[i][v]!=D[i])return false;
return true;
}
bool inPath(int s,Edge*e)
{
rep(i,2)
{
int Dist=min(DistS[i][s]+DistT[i][e->t],DistS[i][e->t]+DistT[i][s])+e->c;
if(Dist!=D[i])return false;
}
return true;
}
bool In[maxn]={};
int A[maxn],v=0,Dp[maxn]={};
bool cmp(int i,int j)
{
return DistS[0][i]<DistS[0][j];
}
int main()
{
//freopen("in","r",stdin);
scanf("%d%d%d%d%d%d",&n,&m,S+0,T+0,S+1,T+1);int s,t,c;
rep(i,2)S[i]–,T[i]–;
while(m–)scanf("%d%d%d",&s,&t,&c),–s,–t,AddEdge(s,t,c);
rep(i,2)Spfa(S[i],DistS[i]),Spfa(T[i],DistT[i]);
rep(i,2)D[i]=DistS[i][T[i]];
rep(i,n)In[i]=inPath(i),In[i]?A[v++]=i:0;
sort(A,A+v,cmp);
int ans=0;
rep(i,v)
{
int t=A[i];ans>?=Dp[t];
for(Edge*e=E[t];e;e=e->next)if(In[e->t]&&inPath(t,e))
Dp[e->t]>?=Dp[t]+e->c;
}
cout<<ans<<endl;
}

[Scoi2010]序列操作

[Scoi2010]序列操作

Time Limit:10000MS  Memory Limit:65536K
Total Submit:38 Accepted:14
Case Time Limit:2000MS

Description

lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作:
0 a b 把[a, b]区间内的所有数全变成0
1 a b 把[a, b]区间内的所有数全变成1
2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0
3 a b 询问[a, b]区间内总共有多少个1
4 a b 询问[a, b]区间内最多有多少个连续的1
对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?

Input

输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目
第二行包括n个数,表示序列的初始状态
接下来m行,每行3个数,op, a, b,(0<=op<=4,0<=a<=b

Output

对于每一个询问操作,输出一行,包括1个数,表示其对应的答案

Sample Input

10 10
0 0 0 1 1 0 1 0 1 1
1 0 2
3 0 5
2 2 2
4 0 4
0 3 6
2 3 7
4 2 8
1 0 5
0 5 6
3 3 9

Sample Output

5
2
6
5

Hint

对于30%的数据,1<=n, m<=1000
对于100%的数据,1<=n, m<=100000

Source

Day2
八中OJ总算好了。。我要开始做题了。。
这道题目本来我以为是简单的线段树问题,结果折腾了我一个半小时,最后我发现我对线段数打标记的理解不够透彻。。
这里的标记有3种,全变0,全变1,相反,关键是如果给一个东西打相反的标记的话,并不是什么真正的标记,而是要改变原有的标记,把全变0跟全变1互换,把相反跟无标记状态互换。。。
在代码中我为了方便把0的数据和1的数据用一个类来表示,再用一个大类表示综合的数据,这样相反只要交换两个数据就OK了。。
Code:
www.ideone.com/XDtvQ

RQNOJ 考分鄙视

http://www.rqnoj.cn/Problem_509.html
这道题目挺水的,如果考试i鄙视考试j,那么Ai-Aj>i-j…就是Ai-i>Aj-j..所以把A’i=Ai-i。。就是A’i的逆序对数量了。。。。直接用树状数组。。
Code:
#include<cstdio>#include<iostream>#define rep(i,n) for(int i=0;i<n;i++)using namespace std;const int maxn=100000,maxv=20000,maxs=maxn+maxv+10,mod=12345;int C[maxs]={},n;void add(int p,int d){ for(;p<maxs;p+=(p&-p)) C[p]+=d;}int sum(int p){ int ret=0; for(;p;p-=(p&-p))ret+=C[p]; return ret;}void Plus(int&a,int b){ a+=b;if(a>=mod)a-=mod;}int main(){ //freopen("in","r",stdin); scanf("%d",&n);int x,ans=0; rep(i,n) { scanf("%d",&x);x+=maxn-i; Plus(ans,sum(x-1)); add(x,1); } printf("%dn",ans);}

RQNOJ 多人背包

www.rqnoj.cn/Problem_123.html
这道题目实际上意思就是求前K优解,效仿一般的动态规划,可以很方便的搞定这个。。只是转移的时候更新的是一个队列。。
Code:

#include<cstdio>#include<algorithm>#include<iostream>#define rep(i,n) for(int i=0;i<n;i++)const int maxk=50+1,maxv=5000+1,maxn=200,inf=~0U>>1;using namespace std;int k,v,n;struct Thing{ int w,v;}T[maxn];int tmp[maxk];struct State{ int n,A[maxk]; State(){n=0;} void put(int x){A[n++]=x;} void Update(State&s,int v) { int m=min(k,s.n+n),i=0,j=0; #define get(i) (s.A[i]+v) A[n]=-inf;s.A[s.n]=-inf; rep(o,m) { if(A[i]>get(j)) tmp[o]=A[i],i++; else tmp[o]=get(j),j++; } memcpy(A,tmp,sizeof(int)*m); n=m; }}Dp[maxv];void Init(){ scanf("%d%d%d",&k,&v,&n); rep(i,n)scanf("%d%d",&T[i].w,&T[i].v);}void Solve(){ Dp[0].put(0); rep(i,n) { for(int j=v;j>=T[i].w;j–) Dp[j].Update(Dp[j-T[i].w],T[i].v); } int ans=0; rep(i,Dp[v].n)ans+=Dp[v].A[i]; cout<<ans<<endl;}int main(){ //freopen("in","r",stdin); Init(); Solve();}

RQNOJ 找第k小的数

哎。。最近状态很颓废啊。。。
这道题题目算法地球人都知道,就是线段树+二分判定,但我WA了N次囧。。。
设当前的区间是l,r。。那么设C(x)为用线段树的在l,r中小于x的个数,那么如果当前要第k小的数,
则C(x)=k-1,同时x属于l和r之间,所以C(x+1)=k。。就是找出最大的C(x)为k-1的数。。。
Code:

#include<cstdio>#include<algorithm>#include<iostream>#define rep(i,n) for(int i=0;i<n;i++)using namespace std;const int maxn=10000+10;#define Left t*2,l,l+r>>1#define Right t*2+1,l+r>>1,rint*T[maxn*4],n,m,A[maxn],d;void Build(int t,int l,int r){ int s=r-l; T[t]=new int[s]; memcpy(T[t],A+l,sizeof(int)*s); sort(T[t],T[t]+s); if(s>1)Build(Left),Build(Right);}int get(int*D,int s,int x){ int i,l; for(i=-1,l=d;l;l>>=1) if(i+l<s&&D[i+l]<x) i+=l; return i+1;}int Count(int t,int l,int r,int a,int b,int x){ if(l>=b||r<=a)return 0; if(l>=a&&r<=b)return get(T[t],r-l,x); return Count(Left,a,b,x)+Count(Right,a,b,x);}#define Cal(x) (Count(1,0,n,a,b,x))int Ask(int a,int b,int k){ int i,l; for(i=n,l=d;l;l>>=1) if(i-l>=0&&Cal(A[i-l])>=k) i-=l; return A[i-1];}int main(){ //freopen("in","r",stdin); scanf("%d%d",&n,&m); rep(i,n)scanf("%d",A+i); Build(1,0,n);sort(A,A+n);A[n]=1e6;int l,r,k; d=1;while(d<=n)d<<=1;d>>=1; while(m–) { scanf("%d%d%d",&l,&r,&k);l–; printf("%dn",Ask(l,r,k)); }}

[AHOI1997]彩旗飘飘

www.rqnoj.cn/Problem_371.html
这道题其实很简单,但我WA了2次。。原因是搞不清楚边界条件。。
实际上往后推的方便多了囧。。一下就AC。。。
Code:
#include <vector>
#include <algorithm>
#include <utility>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#define rep(i,n) for(int i=0;i<n;i++)
#define pb push_back
const int inf=~0U>>1,maxn=15+10;
using namespace std;
typedef long long ll;
ll Dp[maxn][maxn][maxn][2]={};
int main()
{
//freopen("in","r",stdin);
int n,m;cin>>n>>m;
Dp[1][0][0][0]=Dp[0][1][0][1]=1;
for(int l=1;l<n*2;l++)
for(int a=0;a<=min(l,n);a++)
{
int b=l-a;
for(int c=0;c<=m;c++)
for(int l=0;l<2;l++)
if(ll x=Dp[a][b][l])
{
Dp[a+1][b][0]+=x;
Dp[a][b+1][1]+=x;
}
}
cout<<Dp[n][n][m][0]*2<<endl;
}

[AHOI2007]密码箱

www.rqnoj.cn/Problem_296.html
我这个算法的复杂度应该是LogN*Sqrt(N)..居然AC了囧。。。
这题就是求x*x%n==1的解的个数,
也就是n|(x+1)(x-1)..设n=a*b,a<b,枚举a,那么可以考虑a|x+1&&b|x-1的情况,还有一个是对称的,这种情况下,按b的倍数枚举x,由于b>Sqrt(N)。。故最多Sqrt(N)个x,然后用个set去重就OK了。。
Code:
#include <vector>
#include <algorithm>
#include <utility>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <set>
#define rep(i,n) for(int i=0;i<n;i++)
#define pb push_back
const int inf=~0U>>1;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int main()
{
//freopen("in","r",stdin);
ll n;cin>>n;set<int> S;
for(int a=1;a*a<=n;a++)
if(n%a==0)
{
int b=n/a;
for(int x=1;x<n;x+=b)
if((x+1)%a==0)
S.insert(x);
for(int x=b-1;x<n;x+=b)
if((x-1)%a==0)
S.insert(x);
}
for(set<int>::iterator i=S.begin();i!=S.end();i++)
cout<<*i<<endl;
}