[Sdoi2010 粟粟的书架]线段树、归并排序思想、桶

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1926
【算法分析】
前半部分,直接开200*200个1000的桶,利用经典的O(1)取子矩阵和的那种思想。复杂度做到了
O(Q*1000+R*C*1000)。
后半部分,弄一个线段树然后利用归并的思想把它映射到线性表上,然后二分答案再利用线段树二分线性表上的位置,最终得出ans。
时间复杂度O(Q*lg 1000 * lg c * lg c)。

【其它】
Orz。一开始第一部分用二维线段树做,直接TLE到SB。
线性表的话复杂度达到O(Q * lg 1000 * lg r * lg c * lg r*c)。
桶的话复杂度达到O(Q * lg 1000* lg r * lg c)。但是空间受不了。。。
跑得全世界最慢。
1 23199 root 157204K 4435MS Pascal 3.82K 2010-05-14 14:07:37 2 23435 oimaster 100112K 5574MS G++ 4.15K 2010-05-15 22:54:09 3 24878 edward_mj 164928K 9324MS G++ 4.45K 2010-05-21 20:25:45 【CODE】
#include #include #include const int N=205;
const int rate=8;
int m,n,q,spt,a[N][N],b[500005];
int sp[12000005],Tong[N][N][1001];
int S[ 12000005];
struct Node_t{int l,r,st,ed;}Tr[500005*rate];

struct Solve1_t{
int Sum,Limit,Num,x1,y1,x2,y2,h,Lt;
void input(){
spt=0;
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
memset(Tong,0,sizeof(Tong));
int i,j,k;
for (i=1;i<=m;i++)
for (j=1;j<=n;j++){
if (i+j==2) {Tong[i][j][a[1][1]]++;continue;}
if (i>1){
for (k=1;k<=1000;k++)
Tong[i][j][k]=Tong[i-1][j][k];
for (k=1;k<=j;k++)
Tong[i][j][a[i][k]]++;
}else{
for (k=1;k<=1000;k++)
Tong[i][j][k]=Tong[i][j-1][k];
for (k=1;k<=i;k++)
Tong[i][j][a[k][j]]++;
}
}
}

void solve(){
S[0]=0;
for (int i=1;i<=spt;i++) S[i]=S[i-1]+sp[i];
for (int i=0,j;i scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
int Nt;
Sum=Num=0;
for (j=1000;j>=1;j–){
Nt=0;
Nt+=Tong[x2][y2][j];
Nt-=Tong[x1-1][y2][j];
Nt-=Tong[x2][y1-1][j];
Nt+=Tong[x1-1][y1-1][j];
Sum+=Nt*j;
Num+=Nt;
if (Sum>=h){
Num-=(Sum-h)/j;
printf("%dn",Num);
break;
}
}
if (Sum }
}
}Deal1;

struct Solve2_t{
int x1,y1,x2,y2,Num,h,Limit;
int Sum;

void input(){
spt=0;
for (int i=1;i<=n;i++)
scanf("%d",&b[i]);
}

void Union(Node_t &x,Node_t &y){
for (int i=x.st,j=y.st;i<=x.ed || j<=y.ed;)
if (i<=x.ed && (j>y.ed || sp[i] else sp[++spt]=sp[j++];
}

void build(int p,int l,int r){
Tr[p].l=l; Tr[p].r=r;
if (l==r){
Tr[p].st=Tr[p].ed=++spt;
sp[spt]=b[l];
}else{
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
Tr[p].st=spt+1;
Union(Tr[p*2],Tr[p*2+1]);
Tr[p].ed=spt;
}
S[Tr[p].ed]=sp[Tr[p].ed];
for (int i=Tr[p].ed-1;i>=Tr[p].st;i–)
S[i]=S[i+1]+sp[i];
}

void Try(int p){
if (y1<=Tr[p].l && Tr[p].r<=y2){
if (Limit>sp[Tr[p].ed]) return;
int l=Tr[p].st,r=Tr[p].ed,mid;
while (l mid=(l+r)/2;
if (sp[mid] else r=mid;
}
Sum+=S[l];
Num+=Tr[p].ed-l+1;
}else{
int mid=(Tr[p].l+Tr[p].r)/2;
if (y1<=mid) Try(p*2);
if (y2>mid) Try(p*2+1);
}
}

bool can(int tmp){
Limit=tmp;
Sum=Num=0;
Try(1);
if (Sum return true;
}

void solve(){
int l,r,mid;
for (int i=0;i scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
l=1,r=1000;
while (l+1 mid=(l+r)/2;
if (can(mid)) l=mid;
else r=mid-1;
}
if (can(r)) l=r;
if (!can(l)) puts("Poor QLW");
else{
Num-=(Sum-h)/l;
printf("%dn",Num);
}
}
}
}Deal2;

int main(){
freopen("susu.in","r",stdin);
freopen("susu.out","w",stdout);
scanf("%d%d%d",&m,&n,&q);
if (m>1){
Deal1.input();
Deal1.solve();
}
else{
Deal2.input();
Deal2.build(1,1,n);
Deal2.solve();
}
return 0;
}

[Sdoi2010 外星千足虫]高斯消元、位运算压位优化

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1923
【算法分析】
每次相当于加多一个方程组,然后就是高斯消元,但是看着高斯消元那恐怖的复杂度:O(n^3)。
你敢写么。。。
然后我们可以发现,他只有0和1这两种。
这想到了啥?位运算!
我们把方程组的每31位压成1个int类型。
然后消元的时候,一个xor操作,就可以消31个元素!
最后,将除以一个31。
于是可以很快通过。
【时间复杂度】O(m^2*n/32)。
【其它】1A
Orz。。。这次不知道为啥。。。我真的排第一了。。。
而且时间确实比较短。。。
难道是IO的关系?
但是我仅仅用的是scanf么。。。= =
1 24503 edward_mj 1024K 310MS G++ 2.16K 2010-05-19 22:05:42 2 24449 wu3412790 1256K 667MS Pascal 2.59K 2010-05-19 20:39:15 3 24432 maja 1256K 715MS Pascal 2.60K 2010-05-19 20:10:06 4 23270 FDhyc 776K 808MS Pascal 2.32K 2010-05-14 19:26:32 5 23881 gaoxin 872K 965MS Pascal 1.63K 2010-05-17 18:40:25 6 24455 yjq 876K 1013MS Pascal 1.52K 2010-05-19 20:48:03 7 23588 nehzilrz 4056K 1027MS G++ 1.22K 2010-05-16 13:51:32 8 24001 tracyhenry 30356K 1121MS Pascal 1.98K 2010-05-18 13:23:35 9 23657 Apocalypse 808K 2357MS G++ 0.94K 2010-05-16 18:08:46 10 23340 jsn1993 2080K 2372MS G++ 1.16K 2010-05-15 07:53:57 11 23361 oimaster 2064K 2419MS G++ 1.36K 2010-05-15 15:51:45 【CODE】
#include #include #include #include using namespace std;
const int N=2005;
int n,m,ns;
char S[N];
int data[N][N/20],b[N],*a[N],done;
int ans[N];
void output();

void guass(int L){
int i,j,k,step=0,bit=0;
for (i=1;i<=done;i++){
if (a[L][step]&(1< for (j=step;j<=ns;j++)
a[L][j]^=a[i][j];
b[L]^=b[i];
}
bit++; if (bit>31) bit=0,step++;
}
for (i=done+1;i<=L;i++){
k=0;
for (j=i;j<=L;j++)
if (a[j][step]&(1< if (!k) break;
swap(a[i],a[k]);
swap(b[i],b[k]);
done++;
if (done==n) break;
for (j=i+1;j<=L;j++)
if (a[j][step]&(1< for (k=step;k<=ns;k++)
a[j][k]^=a[i][k];
b[j]^=b[i];
}
bit++; if (bit>31) bit=0,step++;
}
}

void solve(){
int i,bit,step,j;
done=0;
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++) a[i]=data[i];
for (i=1;i<=m;i++){
scanf("%s",S+1);
scanf("%d",&b[i]);
bit=0; step=0;
for (j=1;j<=n;j++){
a[i][step]|=(int)(S[j]-‘0’)< bit++; if (bit>31) bit=0,step++;
}
ns=step;
guass(i);
if (done==n){
printf("%dn",i);
output();
return;
}
}
printf("Cannot Determinen");
}

void output(){
int i,j,bit,step,now;
ans[n]=b[n];
for (i=n-1;i>=1;i–){
for (j=1;j<=n;j++) S[j]=0;
bit=step=0;
for (j=1;j<=n;j++){
if (a[i][step]&(1< S[j]=(char)(1);
else
S[j]=(char)(0);
bit++; if (bit>31) bit=0,step++;
}
now=0;
for (j=i+1;j<=n;j++)
now^=ans[j]&(int)(S[j]);
ans[i]=now^b[i];
}
for (i=1;i<=n;i++)
if (ans[i]) puts("?y7M#");
else puts("Earth");
}

int main(){
solve();
}

[Sdoi2010 星际竞速]巧妙的费用流、有向无环图的最小权路径覆盖

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1927
【算法分析】
我感觉挺巧妙的。。。想了好一会儿。
首先注意到,每个点最终的入度都为1。
如果把最终停在这个点不再向后走,看成是向汇点连一条边的话,那么每个点的出度都为1。
然后呢?
我们相当于取N条边。这N条有向边除了和源点相连的以外,都是不重头,不重尾的。
这想到了什么?
匹配!
因为头尾可以看成二分图的两边~于是变成最小权匹配。但是由于与源点相连的可以重合,所以新增一个二分图X部的点,利用网络流给它赋予INF这么大的容量。
然后最小费用流完美解决。
【其它】1A。
Orz。。。好不容易又排第一。。。
1 24143 edward_mj 1004K 1917MS G++ 1.90K 2010-05-18 20:41:38 2 23595 nehzilrz 888K 1964MS G++ 1.68K 2010-05-16 13:54:28 3 23547 oimaster 824K 2091MS G++ 2.48K 2010-05-16 11:05:34 4 23681 gaoxin 1256K 2683MS Pascal 1.95K 2010-05-16 18:44:59 5 23196 root 868K 2714MS G++ 1.47K 2010-05-14 14:05:05 6 23813 FDhyc 1436K 2965MS Pascal 3.32K 2010-05-17 13:33:23 【CODE】
#include #include #include const int E=2000000;
const int N=1000005;
const int INF=1000000000;
struct edge{int x,y,c,w;edge *next,*op;}sp[E],*ls[N],*fa[N];
int n,m,S,T,e,cost;
int d[N],v[N],L[N];

void addedge(int x,int y,int c,int w){
e++;
sp[e].x=x; sp[e].y=y; sp[e].c=c; sp[e].w=w;
sp[e].next=ls[x]; ls[x]=&sp[e]; sp[e].op=&sp[e+1];
e++;
sp[e].x=y; sp[e].y=x; sp[e].c=0; sp[e].w=-w;
sp[e].next=ls[y]; ls[y]=&sp[e]; sp[e].op=&sp[e-1];
}

void input(){
scanf("%d%d",&n,&m);
S=n+n+2; T=S+1;
for (int i=1,x;i<=n;i++){
scanf("%d",&x);
addedge(S-1,i+n,1,x);
}
addedge(S,S-1,INF,0);
for (int i=1,x,y,w,tmp;i<=m;i++){
scanf("%d%d%d",&x,&y,&w);
if (x>y) {tmp=x;x=y;y=tmp;}
addedge(x,y+n,1,w);
}
for (int i=1;i<=n;i++){
addedge(S,i,1,0);
addedge(i+n,T,1,0);
}
}

bool spfa(){
int head=0,tail=1;
for (int i=1;i<=T;i++){
d[i]=INF;
v[i]=0;
}
d[S]=0; v[S]=1; L[1]=S;
while (head!=tail){
head++; if (head>=N) head=0;
for (edge *t=ls[L[head]];t;t=t->next)
if (t->c && d[t->x]+t->w d[t->y]=d[t->x]+t->w;
fa[t->y]=t;
if (!v[t->y]){
v[t->y]=1;
tail++; if (tail>=N) tail=0;
L[tail]=t->y;
}
}
v[L[head]]=0;
}
if (d[T]==INF) return false;
return true;
}

void change(){
int nf=INF;
for (int i=T;i!=S;i=fa[i]->x)
if (fa[i]->c nf=fa[i]->c;
cost+=nf*d[T];
for (int i=T;i!=S;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}
}

int main(){
input();
cost=0;
while (spfa()) change();
printf("%dn",cost);
}

[Sdoi2010 地精部落]动态规划

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1925
【算法分析】
F[i][j][k]表示第i段,小于当前选择的那个数的个数是j,k代表是下一个该上升还是下降,的方案数。
然后转移就行了。
然后需要用滚动数组优化。
【时间复杂度】O(n^2)
【空间复杂度】O(n)
【CODE】
#include #include #include #include using namespace std;
const int N=4205;
int n,mod;
int F[2][N][2];

void dp(){
int i,j,k,p,rest,Sum;
memset(F,0,sizeof(F));
for (i=0;i for (i=2;i<=n;i++){
p=i%2; rest=n-i;
for (j=0;j<=rest;j++){
F[p][j][0]=0;
F[p][j][1]=0;
}
// Deal up
Sum=0;
for (j=0;j<=rest;++j){
Sum+=F[1-p][j][1]; if (Sum>=mod) Sum%=mod;
F[p][j][0]=Sum;
}
// Deal down
Sum=F[1-p][rest+1][0];
for (j=rest;j>=0;–j){
F[p][j][1]=Sum;
Sum+=F[1-p][j][0]; if (Sum>=mod) Sum%=mod;
}
}
Sum=0;
for (i=0;i<=rest;i++){
Sum+=F[p][i][0]; if (Sum>=mod) Sum%=mod;
Sum+=F[p][i][1]; if (Sum>=mod) Sum%=mod;
}
cout << Sum << endl;
}

int main(){
cin >> n >> mod;
if (n==1) cout << 1 % mod << endl;
else dp();
}

[Sdoi2010 大陆争霸]二叉堆、拓扑序、畸形最短路

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1922
【算法分析】
这个显然必须按照”保护图“的拓扑序来搞。
然后我就模拟那个”保护图“拓扑的过程。
不过期间用最小堆来存放结点的d[i]值。
堆中存放的都是入度为0,且尚未扩展的点。
然后每次取堆顶元素拿出来扩展。
扩展方法如下。
1、
首先把”保护图“中的边弄掉。(其实就是拓扑排序的过程)
然后如果出现可新加入的点,那么update一下他,然后把他放入堆。
update(i)的意思是:d[i]=min(max(limit,d[x]+w)) 其中limit为当前点的d值。
x为在”时间图“中有边x->i的点。w就是该边对应的权值了。
2、
在”时间图“里扩展,假如有我能到的点,而且他的入度以为0,那么可以min一下我走过去的时间。

然后最后输出d[n]。
实际上第一种扩展方式是第二种的漏掉的补充,YY一下就能明白。
【时间复杂度】O((n+m) lg n)
【其它】
泪奔。。。好久没排过第一了。时间是最快的。。。当然,其实挺接近。
然后最后一个是我徒弟~
他的方法完全暴力。
k次spfa,每次判整个d数组是否和前一次的不同,然后拓扑扩展一下,将本次在队列里的点的保护去除。
Orz。。。这样的都能过。。。
1 23953 edward_mj 2392K 326MS G++ 3.06K 2010-05-17 22:02:49 2 23587 nehzilrz 12700K 373MS G++ 1.29K 2010-05-16 13:51:06 3 23691 gaoxin 13488K 466MS Pascal 1.18K 2010-05-16 19:11:12 4 23360 oimaster 1060K 513MS G++ 1.56K 2010-05-15 15:50:58 5 23339 jsn1993 1244K 591MS G++ 1.10K 2010-05-15 07:53:14 6 23184 root 45784K 1152MS G++ 1.14K 2010-05-14 13:40:08 7 23188 FDhyc 45784K 1230MS G++ 1.14K 2010-05-14 13:56:02 8 23936 54525871 2064K 2013MS Pascal 2.48K 2010-05-17 21:12:32 【CODE】
#include #include #include #define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
typedef __int64 lld;
const int E=1000005;
const int N=3005;
const int INF=1000000000;
int n,m;
int du[N],zz[N];
lld d[N];

struct edge{int x,y,w;edge *next;};
struct Link_t{
int e;
edge sp[E],*ls[N];
void init(){
e=0;
for (int i=0;i }

void add(int x,int y,int w){
e++;
sp[e].x=x; sp[e].y=y; sp[e].w=w; sp[e].next=ls[x];
ls[x]=&sp[e];
}
}Graph,Protect,Graphop;

struct Heap_t{
int tot,a[N],tmp;

void swap(int x,int y){
tmp=a[x]; a[x]=a[y]; a[y]=tmp;
zz[a[x]]=x; zz[a[y]]=y;
}

void up(int k){
while (k>1 && d[a[k/2]]>d[a[k]]){
swap(k/2,k);
k/=2;
}
}

void down(int k){
int p;
while (k*2<=tot){
p=k*2;
if (p+1<=tot && d[a[p+1]] if (d[a[p]] swap(p,k);
k=p;
}else break;
}
}

void ins(int key){
a[++tot]=key;
zz[key]=tot;
up(tot);
}

void del(){
swap(1,tot);
zz[a[tot]]=0;
tot–;
if (tot<1) return;
down(1);
}

void repair(int p){
up(p);
down(p);
}
}heap;

void input(){
Graph.init();
Protect.init();
Graphop.init();
memset(du,0,sizeof(du));
scanf("%d%d",&n,&m);
for (int i=1,x,y,w;i<=m;i++){
scanf("%d%d%d",&x,&y,&w);
Graph.add(x,y,w);
Graphop.add(y,x,w);
}
for (int i=1,Num,x,j;i<=n;i++){
scanf("%d",&Num);
for (j=1;j<=Num;j++){
scanf("%d",&x);
Protect.add(x,i,0);
du[i]++;
}
}
}

void update(int p,lld limit){
for (edge *t=Graphop.ls[p];t;t=t->next)
if (max(d[t->y]+t->w,limit) d[p]=max(d[t->y]+t->w,limit);
}

void topsort(){
int i;
edge *t;

for (i=1;i<=n;i++) d[i]=(lld)(INF)*INF;
d[1]=0;
heap.tot=0;

for (i=1;i<=n;i++)
if (!du[i]) heap.ins(i);

while (heap.tot>0){
int x=heap.a[1];
heap.del();
for (t=Protect.ls[x];t;t=t->next){
du[t->y]–;
if (!du[t->y]){
update(t->y,d[x]);
heap.ins(t->y);
}
}
for (t=Graph.ls[x];t;t=t->next)
if (!du[t->y] && d[t->y]>d[t->x]+t->w){
d[t->y]=d[t->x]+t->w;
heap.repair(zz[t->y]);
}
}
}

int main(){
input();
topsort();
printf("%I64dn",d[n]);
return 0;
}