/*
树状数组
这道题的操作有三种,插入和删除并到一个函数里完成 ,查询的操作要用到二分的方法。
*/
#include <cstdio>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int M = 100005;
int num[M];
int lowbit( int x )
{
return x&(-x);
}
void add( int x , int val )
{
while( x < M )
{
num[x] += val;
x += lowbit(x);
}
}
int sum( int x )
{
int ret = 0;
while( x > 0 )
{
ret += num[x];
x -= lowbit(x);
}
return ret;
}
int find( int pos , int k )
{
int neww = sum(pos);
int l , r , m;
l = pos + 1;
r = M - 1;
int ans = M;
int total = 0;
while( l <= r )
{
m = ( l+r ) / 2;
total = sum(m) - neww;
if( total >= k )
{
r = m - 1;
if( m < ans )
ans = m;
}
else
l = m + 1;
}
return ans;
}
int main( )
{
int n;
int c,x,y;
while( scanf("%d",&n) != EOF )
{
memset( num , 0 , sizeof(num) );
while( n-- ){
scanf("%d",&c);
if( c == 0 ){
scanf("%d",&x);
add( x , 1 );
}
else if( c == 1 ){
scanf("%d",&x);
if( sum(x) - sum(x-1) == 0 )
printf("No Elment!\n");
else
add( x , -1 );
}
else if( c == 2 ){
scanf("%d%d",&x,&y);
int ret = find( x , y );
if( ret == M )
printf("Not Find!\n");
else
printf("%d\n",ret);
}
}
}
return 0;
}
敌兵布阵
#include<cstdio>
#include<iostream>
using namespace std;
#define maxn 50005
int tree[maxn<<2];
void build(int left,int right,int root)
{
if (left == right)
{
scanf("%d",&tree[root]);
return ;
}
int mid = (left + right) >> 1;
build(left , mid , root << 1);
build(mid + 1 , right , root << 1 | 1);
tree[root]=tree[root<<1]+tree[root<<1|1];
}
int find(int from,int to,int left,int right,int root)
{
if(from<=left&&right<=to)
{
return tree[root];
}
int mid=(left+right)>>1;
int sum=0;
if(from<=mid)sum+=find(from,to,left,mid,root<<1);
if(to>mid)sum+=find(from,to,mid+1,right,root<<1|1);
return sum;
}
void update(int p,int add,int left,int right,int root)
{
if(left==right)
{
tree[root]+=add;
return ;
}
int mid=(left+right)>>1;
if(p<=mid)update(p,add,left,mid,root<<1);
else update(p,add,mid+1,right,root<<1|1);
tree[root]=tree[root<<1]+tree[root<<1|1];
}
int main()
{
//freopen("in.txt","r",stdin);
int cas;
char s[10];
scanf("%d",&cas);
for(int i=1;i<=cas;i++)
{
int n;
printf("Case %d:\n",i);
scanf("%d",&n);
build(1,n,1);
while(scanf("%s",s))
{
int x,y;
if(s[0]=='E')break;
scanf("%d%d",&x,&y);
if(s[0]=='A')update(x,y,1,n,1);
else if(s[0]=='Q')printf("%d\n",find(x,y,1,n,1));
else update(x,-y,1,n,1);
}
}
return 0;
}
Median Filter
/*
我们按“S”型枚举每一个点,如左图:即右->下->左->下->右....
用一个集合存储当前子正方形内的数,下一次查找时,插入新的一列,删除旧的列。
如右图:当前枚举点150(三角标记),黑色正方形的值保存一个集合中,求出中位数。
当枚举的点有150移动到125时,我们把蓝色椭圆内的点插入,把黄色椭圆内的点删掉,
此时集合中的元素即为红色正方形内的元素,可求出新的中位数。不断如此操作,直到结束。
首先分析一下复杂度:子正方形边长L=2*r+1,N=L*L枚举每个点,需要(500-L)* (500-L)
下面主要的问题是如何快速求中位数?
如果每次排序找中位数,需要Nlog(N),复杂度为:(500-L)* (500-L)* Nlog(N),
9<=N<250000,这么大的复杂度会超时。所以我们在查中位数时尽量降低复杂度。
树状数组和线段树都可以实现查找第k大数。线段树插入、查找均为log(N),
树状数组取决于二进制中1的个数,所以实际比log(N)还要快。
若用线段树,每次O(L*lgN)的插入,O(L*lgN)的删除,O(lgN)的查找。但写完后超时,
好像线段树系数很大,实现的时候不能完全达到log
用树状数组,每次O(L *lgN)的插入,O(L*lgN)的删除,O((lgN)^2)的查找,
C++1.5秒可过。在查找时如果不用二分查找,也可优化为O(lgN)的查找,效率会更高。
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int rr, c[1111111];
int lowbit(int x)
{
return x & (-x);
}
void add(int i, int val)
{
while(i <= rr){
c[i] += val;
i += lowbit(i);
}
}
int sum(int i)
{
int s = 0;
while(i > 0){
s += c[i];
i -= lowbit(i);
}
return s;
}
int mat[555][555];
int ans[555][555];
int mm;
int Bin()
{
int l = 1, r = rr;
while(l < r){
int m = (l + r) >> 1;
if(sum(m) >= mm) r = m;
else l = m + 1;
}
return l - 1;
}
int main()
{
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout);
int n, r;
while(scanf("%d %d", &n, &r) == 2){
if(!n && !r) {
break;
}
r = (r << 1) + 1;
mm = (r * r + 1) >> 1;
rr = -1;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++){
scanf("%d", &mat[i][j]);
rr = max(rr, mat[i][j] + 1);
}
memset(c, 0, sizeof(c));
for(int i = 0; i < r; i++){
for(int j = 0; j < r - 1; j++){
add(mat[i][j] + 1, 1);
}
}
for(int i = 0; ; ){
for(int j = r - 1; j < n; j++){
for(int k = 0; k < r; k++)
add(mat[i + k][j] + 1, 1);
ans[i][j - (r - 1)] = Bin();
for(int k = 0; k < r; k++)
add(mat[i + k][j - (r - 1)] + 1, -1);
}
for(int j = n - 1; j >= n - (r - 1); j--)
add(mat[i][j] + 1, -1);
i++;
if(i + (r - 1) >= n) break;
for(int j = n - 1; j >= n - (r - 1); j--)
add(mat[i + (r - 1)][j] + 1, 1);
for(int j = n - r; j >= 0; j--){
for(int k = 0; k < r; k++)
add(mat[i + k][j] + 1, 1);
ans[i][j] = Bin();
for(int k = 0; k < r; k++)
add(mat[i + k][j + (r - 1)] + 1, -1);
}
for(int j = 0; j < (r - 1); j++)
add(mat[i][j] + 1, -1);
i++;
if(i + (r - 1) >= n) break;
for(int j = 0; j < (r - 1); j++)
add(mat[i + (r - 1)][j] + 1, 1);
}
for(int i = 0; i <= n - r; i++){
for(int j = 0; j <= n - r; j++){
//if(j) printf(" ");
printf("%d ", ans[i][j]);
}
printf("\n");
}
}
return 0;
}
How far away ?
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define MAX 40000
struct edge{
int v,w;
};
vector<edge> mp[MAX];
vector<edge> query[MAX];
bool flag[MAX];
int pre[MAX],father[MAX],path[MAX];
int find(int x){
return x==pre[x]? x:pre[x]=find(pre[x]);
}
void LCA(int k)
{
int i,j;
for(i=0;i<mp[k].size();i++)
{
int a=mp[k][i].v;
if(!flag[a])
{
flag[a]=1;
path[a]=path[k]+mp[k][i].w;
LCA(a);
pre[a]=k;
for(j=0;j<query[a].size();j++)
{
int b=query[a][j].v;
if(flag[b]&&father[query[a][j].w]==-1)
{
if(a==b) father[query[a][j].w]=0;
else father[query[a][j].w]=path[a]+path[b]-2*path[find(b)];
}
}
}
}
}
int main()
{
int i,j,k,T,n,m,a,b;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
mp[i].clear();
query[i].clear();
flag[i]=0;
father[i]=-1;
pre[i]=i;
path[i]=0;
}
int a,b,c;
edge X;
for(i=1;i<n;i++)
{
scanf("%d%d%d",&a,&b,&c);
X.v=b;X.w=c;
mp[a].push_back(X);
X.v=a;
mp[b].push_back(X);
}
for(i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
X.v=b;X.w=i;
query[a].push_back(X);
X.v=a;
query[b].push_back(X);
}
flag[1]=1;
// path[1]=0;
LCA(1);
for(i=1;i<=m;i++)
printf("%d\n",father[i]);
}
return 0;
}
Connections between cities
/*
这里要用到LCA,我们的距离为两个子节点到根的距离和减去最近祖先的距离的2倍;
*/
#include<cstdio>
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
class node
{
public:
int en;
int len;
};
vector<node>tree[10024],Qes[10024];
int dist[10024],set[10024],visit[10024],ans[1000024];
void init( int n )
{
for(int i=0;i<=n;i++)
{
dist[i]=0;
set[i]=i;
visit[i]=0;
// indegree[i]=0;
tree[i].clear( );
Qes[i].clear( );
}
}
inline int find( int x )
{
return set[x]==x?x:set[x]=find( set[x] );
}
inline void LCA( int num,int d,int root )
{
dist[num]=d;
set[num]=num;
visit[num]=root;//标记为那棵树的根
int size=tree[num].size();
for( int i=0; i<size; i++ )
{
if( !visit[tree[num][i].en] )
{
LCA( tree[num][i].en,tree[num][i].len+d ,root);
set[tree[num][i].en]=num;
}
}
size=Qes[num].size( );
for( int i=0;i<size; i++ )
{
if( visit[Qes[num][i].en] )
{
if( visit[Qes[num][i].en]==root )//如果被标记过并且是同一棵树,就找最近祖先
{
ans[Qes[num][i].len]=dist[num]+dist[Qes[num][i].en]-2*dist[find( Qes[num][i].en )];
}
else ans[Qes[num][i].len]=-1;//要注意为-1,不能为0同点为0
}
}
}
int main( )
{
int n,m,Case,x,y,dis,N;
while( scanf( "%d %d %d",&N,&n,&m )==3 )
{
init( N );
for( int i=1; i<= n ;i++ )
{
scanf( "%d %d %d",&x,&y,&dis );
node T;
T.en = y;
T.len = dis;
tree[x].push_back( T );
// indegree[y]++;
T.en=x;
tree[y].push_back( T );
}
for( int i=1;i <= m ;i++ )
{
scanf( "%d %d",&x,&y );
node T;
T.en=y;
T.len=i;
// ans[i]=-1;
Qes[x].push_back( T );
T.en=x;
Qes[y].push_back( T );
}
for( int i=1;i<=N; i++ )
{
if( visit[i]==0 )//多棵树的情况,因为同一棵树任何一个节点都可以做根节点
{
LCA( i,0,i );
}
}
for( int i=1;i<=m ; i++ )
{
if( ans[i]==-1 )
{
printf( "Not connected\n" );
}
else printf( "%d\n",ans[i] );
}
}
return 0;
}
Interviewe
/*
RMQ
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200010, A=1001;
int a[N];
int n,k;
int st[N][20],lg2[N];
void ST(int *a,int n)
{
lg2[0]=-1;
for(int i=1;i<=n;i++)
lg2[i]=lg2[i-1]+(i&(i-1)?0:1);
for(int i=0;i<n;i++) st[i][0]=a[i];
for(int j=1;j<=lg2[n];j++)
for(int i=0;lg2[n-i]>=j;i++)
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
int RMQ(int x,int y)
{
int k=lg2[y-x+1];
return max(st[x][k],st[y-(1<<k)+1][k]);
}
bool ok(int c)
{
int t=n/c, s=0;
for(int i=0;i<c;i++) s+=RMQ(t*i,t*i+t-1);
return s>k;
}
int main()
{
while(scanf("%d%d",&n,&k),n>=0 || k>=0)
{
int s=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
s+=a[i];
}
if(s<=k) {printf("-1\n"); continue;}
ST(a,n);
int l=1, r=n;
while(l<r)
{
int m=(l+r)/2;
if(ok(m)) r=m;
else l=m+1;
}
printf("%d\n",l);
}
return 0;
}
Check Corners
/*
本题碉堡了,内存限制好紧,再大一点都不行,还只能用int,开始用short WA了好多次。。。。。
题意:给一个矩阵,然后给Q个询问,每个询问有四个数,分
别代表询问的子矩阵的左上角和右下角,然后找出子矩阵的最大值输出,然后再把这个值与
子矩阵的四个角的值比较,如果有至少一个等于这个最大值就输出“yes”,否则输出“no”。
*/
#include <stdio.h>
#include <iostream>
#include <math.h>
using namespace std;
const int N = 302;
int n, m;
int val[N][N];
int dpmax[N][N][9][9];
void ST()
{
int i, j, r, c, k1, k2;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
dpmax[i][j][0][0]=val[i][j];
k1=(int)(log(double(n))/log(2.0));
k2=(int)(log(double(m))/log(2.0));
for(i=0;i<=k1;i++)
{
for(j=0;j<=k2;j++)
{
if(i==0&&j==0) continue;
for(r=1;r+(1<<i)-1<=n;r++)
{
for(c=1;c+(1<<j)-1<=m;c++)
{
if(i==0)
dpmax[r][c][i][j]=max(dpmax[r][c][i][j-1],dpmax[r][c+(1<<(j-1))][i][j-1]);
else
dpmax[r][c][i][j]=max(dpmax[r][c][i-1][j],dpmax[r+(1<<(i-1))][c][i-1][j]);
}
}
}
}
}
int query(int r1, int c1, int r2, int c2)
{
int kr=(int)(log(double(r2-r1+1))/log(2.0));
int kc=(int)(log(double(c2-c1+1))/log(2.0));
int t1=dpmax[r1][c1][kr][kc];
int t2=dpmax[r2-(1<<kr)+1][c1][kr][kc];
int t3=dpmax[r1][c2-(1<<kc)+1][kr][kc];
int t4=dpmax[r2-(1<<kr)+1][c2-(1<<kc)+1][kr][kc];
return max(max(t1,t2),max(t3,t4));
}
int main()
{
int i, j, k;
int r1,c1,r2,c2;
while(~scanf("%d%d",&n,&m))
{
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&val[i][j]);
ST();
scanf("%d",&k);
while(k--)
{
scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
int ret=query(r1,c1,r2,c2);
printf("%d ",ret);
if(val[r1][c1]==ret||val[r1][c2]==ret||val[r2][c1]==ret||val[r2][c2]==ret) puts("yes");
else puts("no");
}
}
return 0;
}
Cow Sorting
/*
话说这道题要用的三个树状数组,不容易啊。我刚开始想的时候想明白了用公式怎么算,
却想不出来怎么转化到树状数组上,总感觉有些地方实现不了,原来竟然是用三个树状数组。。。
这让只写过一个树状数组的孩纸情何以堪?
具体来说,有一个num数组,里面记录的是插入a[i]后,
在a[i]之前插入且比a[i]小的数的个数;还有一个totalsum数组,
记录的是插入第i个数后,前i-1个数的总和;还有一个smallersum数组,
记录的是插入a[i]后,在a[i]之前插入且比a[i] 小的数的总和。
这样最后就可以算出来了。
*/
#include <cstdio>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int M = 100010;
__int64 num[M] , totalsum[M] , smallsum[M];
__int64 lowbit( __int64 x )
{
return x&(-x);
}
void add( __int64 x[] , __int64 xx , __int64 yy )
{
while( xx < M )
{
x[xx] += yy;
xx +=lowbit(xx);
}
}
__int64 sum( __int64 x[] , __int64 xx )
{
__int64 ss = 0;
while( xx > 0 )
{
ss += x[xx];
xx -= lowbit(xx);
}
return ss;
}
int main( )
{
__int64 n;
while( ~scanf("%I64d",&n) )
{
memset( num , 0 , sizeof(num) );
memset( totalsum , 0 , sizeof(totalsum) );
memset( smallsum , 0 , sizeof(smallsum) );
__int64 x , ans = 0 , count = 0;
for( __int64 i=1 ; i<=n ; i++ ){
scanf("%I64d",&x);
add( num , x , 1 );
add( totalsum , i , x );
add( smallsum , x , x );
count = sum( num , x-1 );
ans += (i-count-1)*x;
ans += sum(totalsum,i-1);
ans -= sum(smallsum,x-1);
}
printf("%I64d\n",ans);
}
return 0;
}