【JLOI2013合集】BZOJ3090 赛车 BZOJ3091 卡牌游戏 BZOJ3092 删除物品 BZOJ3093 地形生成

赛车:

这个题就是水平可见直线、、

水平可见直线怎么做呢、、

就是把所有直线按斜率排序、然后从前往后处理边、

当中维护一个栈、如果当前线和栈顶的交点在栈顶和栈顶-1的交点左边、那么弹掉栈顶、、

(可以这么理解、、就是栈顶超越栈顶-1之前已经被当前线超越、、所以永无翻身之日了、、

然后露出一个点也算的话会导致一些奇葩的情况、、特判处理一下就好了、、

特别有一个就是交点横坐标如果<0要掐掉、、、

 

Code:

var
  np:boolean;
  v:array [0..10001] of boolean;
  k,b:array [0..10001] of longint;
  fr,s:array [0..10001] of longint;
  n,r,i,ran:longint;
  te:longint;
procedure sort(l,r:longint);
  var
    i,j:longint;
    x,y:longint;
  begin
    i:=l;j:=r;ran:=l+random(r-l+1);
    x:=k[ran];y:=b[ran];
    repeat
      while (k[i]<x) or ((k[i]=x) and (b[i]<y)) do inc(i);
      while (x<k[j]) or ((x=k[j]) and (y<b[j])) do dec(j);
      if i<=j then
        begin
          te:=k[i];k[i]:=k[j];k[j]:=te;
          te:=b[i];b[i]:=b[j];b[j]:=te;
          ran:=fr[i];fr[i]:=fr[j];fr[j]:=ran;
          inc(i);dec(j);
        end;
    until i>j;
    if i<r then sort(i,r);
    if j>l then sort(l,j);
  end;
begin
  readln(n);
  for i:=1 to n do read(b[i]);
  for i:=1 to n do read(k[i]);
  for i:=1 to n do fr[i]:=i;
  sort(1,n);
  r:=1;i:=1;
  while (i<n) and (k[i]=k[i+1]) and (b[i]<>b[i+1]) do inc(i);
  s[1]:=i;inc(i);k[0]:=-1;
  while i<=n do
    begin
      while (r>0) and (k[i]=k[s[r]]) and (b[i]<>b[s[r]]) do dec(r);
      if (k[i]<>k[s[r]]) then
        begin
          while (r>0) and ((b[i]-b[s[r]])/(k[s[r]]-k[i])<0) do dec(r);
          while (r>1) and ((b[i]-b[s[r]])/(k[s[r]]-k[i])<(b[s[r]]-b[s[r-1]])/(k[s[r-1]]-k[s[r]])) do dec(r);
        end;
      inc(r);s[r]:=i;
      inc(i);
    end;
  fillchar(v,sizeof(v),false);
  writeln(r);
  for i:=1 to r do
    v[fr[s[i]]]:=true;
  np:=false;
  for i:=1 to n do
    if v[i] then
      begin
        if not np then np:=true else write(' ');
        write(i);
      end;
  writeln;
end.

  

卡牌游戏:

有一个很重要的性质:当前人获胜的概率只与其在排列中与庄家的相对位置和人数有关、、跟具体有哪些人无关、、

那么我们可以用f[i][j]表示还有i人时从庄家开始数第j个人获胜的概率、、

于是可以枚举当前每种可能然后从f[i-1][*]转移、、这就可以写成一个DP了、、

具体实现的时候我用的是记忆化搜索、、所以可能常数大了点?

 

Code:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <ctime>
#include <cstring>
 
using namespace std;
 
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define PII pair<int,int>
#define max(a,b) ((a>b)?(a):(b))
#define min(a,b) ((a,b)?(a):(b))
#define pb push_back
#define mp make_pair
#define X first
#define Y second
#define fill(x) memset(x,0,sizeof x)
 
double f[100][100];
bool v[100][100];
int mov[100];
int n,m;
 
double calc(int tot,int cur){
    if  (v[tot][cur])   return  f[tot][cur];
    if  (tot==1)    return  1.0;
    double now=0;
    int tar;
    rep(i,1,m){
        tar=mov[i]%tot+1;
        int xd=0;
        while   (tar!=cur){
            xd++;
            tar=tar%tot+1;
        }   
        if  (xd)    now+=1.0*calc(tot-1,xd)/m;
    }
    f[tot][cur]=now;
    v[tot][cur]=true;
    return  now;
}
 
int main(){
    scanf("%d%d",&n,&m);
    rep(i,1,m)  scanf("%d",&mov[i]),mov[i]--;
    if  (n==1){
        puts("100.00%");
        return  0;
    }
    rep(i,1,n)  f[n][i]=calc(n,i);
    rep(i,1,n-1)
        printf("%.2lf",f[n][i]*100),cout <<"% ";
    printf("%.2lf",f[n][n]*100);cout <<"%";printf("\n");
    return  0;
}

  

删除物品:

这个题目看上去和实际上的难度差很多啊、、、

因为两个堆只会在顶上转移元素、、不难想到可以把两个顶相对接起来、、这样只要维护一个堆顶就可以描述当前局面、、

然后因为只能很傻地把一个物品上面覆盖的所有物品全移走才可以删除它、、所以必须要快速统计出一个物品到其所在堆顶的物品件数、、于是想到BIT或者线段树、、

接下来要做的就是按价值从高到低模拟删除就可以了、、

注意开longlong、

 

Code:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <ctime>
#include <cstring>
 
using namespace std;
 
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define PII pair<int,int>
#define max(a,b) ((a>b)?(a):(b))
#define min(a,b) ((a,b)?(a):(b))
#define pb push_back
#define mp make_pair
#define X first
#define Y second
#define fill(x) memset(x,0,sizeof x)
 
int l1,l2,mid,n;
long long ans=0;
int c[200010];
 
struct node{
    int n,p;
}a[200010];
 
bool operator <(node aa,node bb){return  aa.n<bb.n;}
 
void add(int x,int delta){for   (int i=x;i<=l1+l2;i+=(i&(-i)))   c[i]+=delta;}
int sum(int x)  {int te=0;for   (int i=x;i>0;i-=(i&(-i)))    te+=c[i];return te;}
 
int main(){
    scanf("%d%d",&l1,&l2);
    rep(i,1,l1){
        scanf("%d",&a[i].n);
        a[i].p=l1-i+1;
    }
    rep(i,l1+1,l1+l2){
        scanf("%d",&a[i].n);
        a[i].p=i;
    }
    mid=l1;
    sort(a+1,a+l1+l2+1);
    rep(i,1,l1+l2)  add(i,1);
    per(i,l1+l2,1){
        if  (a[i].p<=mid){
            ans+=sum(mid)-sum(a[i].p);
            mid=a[i].p;
        }
        else{
            ans+=sum(a[i].p-1)-sum(mid);
            mid=a[i].p-1;
        }
        add(a[i].p,-1);
    }
    printf("%lld\n",ans);
}

  

地形生成:

总体评价同上、、

因为最后序列跟插入顺序关系不大、、所以可以先把所有的山从高到低排序之后插入、、

于是每个山进来的时候都是最低的、、那么它所能插入的位置就是min(cur+1,key+1),cur是当前已经有的山数、、key是关键字、、

如果每个山的高度不同、、累乘即可、问题就已经解决了、、

……

可惜不是、、

那么对于第一问、高度相同的山可以直接加入到cur中、、于是很好解决、

对于第二问、、

第一个性质:同样高度的山插入后不会影响总共的候选位置数、换句话说、就是第一个这样高度的山可以插入cur+1个位置、后面所有等高的也是cur+1个位置、、这个不难证明、、

由这个性质我们得到同高度的山也是可以按任意顺序插入的、

再次利用排序思想、让所有同样高度的山按key从小到大处理、

那么我们如果以右为正方向、每个山的可行插入区间一定是从左端开始且右端连续不降的、

0    key[1]    key[2]&key[3]     ……

|------|-----------|              ……

---------------------------------------------------------

T_T我不会画图、、大致就是这个意思、、2和3的可行区间相等并且包含了1的可行区间、、

因为我们最后只要统计所有本质不同的插入方法、即新高度在每个可插入位置上的不同分布数量集合数、

可以用f[i][j]表示第i个山加入在j位置的方案数、

显然f[i][j]=sigma(f[i-1][k],1<=k<=j)、

最后对f[这种山的数量][*]求和之后累乘起来就可以了、

至于上面这个式子如果直接算最差会到O(n^3)、、不过数据比较有良心是能过的、

于是大家可以视天气情况加BIT或者滚动之类的、

 

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <queue>

#define ps system("pause")
#define message printf("*\n")
#define pb push_back
#define X first
#define Y second
#define PII pair<int,int>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)

typedef long long ll;

using namespace std;

map<int,int> ocr;
int h[1010],key[1010],nfc[1010];
int f[1010][1010];
int n;

int pww(int x,int k){
	int te=x,res=1;
	for	(;k;k/=2)	{if	(k&1)	res=res*te%2011;te=te*te%2011;}
	return	res;
}

int main(){
	//freopen("terrain.in","r",stdin);freopen("terrain.out","w",stdout);
	nfc[0]=1;
	rep(i,1,1000)	nfc[i]=nfc[i-1]*pww(i,2009)%2011;
	scanf("%d",&n);
	rep(i,1,n)	scanf("%d%d",&h[i],&key[i]),ocr[h[i]]++,key[i]--;
	rep(i,1,n)	rep(j,i+1,n)
		if	(h[i]>h[j] || (h[i]==h[j] && key[i]<key[j])){
			swap(h[i],h[j]);
			swap(key[i],key[j]);
		}
	int hp=0,ans1=1,ans2=1;
	for	(int i=n;i;){
		int te=0,cur=0;
		rep(j,0,ocr[h[i]]-1){
			ans1=ans1*(min(hp+1,key[i-j]+1)+te)%2011;te++;
		}
		memset(f[0],0,sizeof f[0]);
		f[0][1]=1;
		rep(j,0,ocr[h[i]]-1){
			memset(f[1-cur],0,sizeof f[1-cur]);
			int sum=0;
			rep(k,1,min(hp+1,key[i-j]+1)){
				sum=(sum+f[cur][k])%2011;
				f[1-cur][k]=sum;
			}
			cur=1-cur;
		}
		te=0;
		rep(j,1,hp+1)	te=(te+f[cur][j])%2011;
		ans2=ans2*te%2011;
		hp+=ocr[h[i]];i-=ocr[h[i]];
	}
	printf("%d %d\n",ans1,ans2);
}

  

你可能感兴趣的:(ZOJ)