12
19
2015
0

Contest20151217滚粗记

又是一场JSOI(JS没事考那么多场干嘛,闲的蛋疼吗)

T1

看起来很水啊,似乎贪心就可以了嘛。

然而,一秒后发现反例……

再一看似乎是DP,不过是哪种类型呢。。好像是邮局dp(pol取的名字)

一分钟后又发现反例。。

然而这是一道区间dp。

首先把每个敌机抽象为一条a[i]-b[i]的线段,问题便转化为切断所有线段的最小代价。

对于区间[l,r],找到包含在区间内的代价最大的线段,枚举上面的点进行切割,然后分成两个小区间,这样就可以转移辣~

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 610
int a[N],b[N],c[N],p[N],d[N],f[11000],dp[N][N];
bool cmp(int x,int y){
    return d[x]<d[y];
}
int r,n,m,k,l,t,s,cnt,cas;
int main(){
    memset(f,0,sizeof(f));
    scanf("%d",&n);
    cnt=0;
    for (int i=1;i<=n;i++)scanf("%d%d%d",&a[i],&b[i],&c[i]);
    for (int i=1;i<=n;i++){d[++cnt]=a[i];d[++cnt]=b[i];p[i*2]=i*2;p[i*2-1]=i*2-1;}
    sort(p+1,p+2*n+1,cmp);
    cnt=0;
    for (int i=1;i<=2*n;i++){
        if (d[p[i]]!=d[p[i-1]]) cnt++;
        f[d[p[i]]]=cnt;
    }
    memset(dp,10,sizeof(dp));
    for (int i=1;i<=cnt+1;i++) dp[i][i-1]=0;
    for (int t=1;t<=cnt;t++)
        for (int i=1;i<=cnt-t+1;i++){
            int j=i+t-1;
            int mx=0;
            for (int k=1;k<=n;k++)
                if ((f[a[k]]>=i)&&(f[b[k]]<=j))
                    if (c[k]>mx) {mx=c[k];l=k;}
            if (mx==0) dp[i][j]=0;
            for (int k=f[a[l]];k<=f[b[l]];k++)
                dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+mx);
        }
    printf("%d\n",dp[1][cnt]);
}

T2

此乃狗题(因为连gonens大神都不会做)

目前只有60分算法,对于每个点做dfs,线段树维护根到当前点有哪些边,统计答案即可。

 

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 50010
#define M 1000010
int head[N],vet[N],cost[N],next[N],tree[4*M];
int num,n,m,k,l,t,x,y,z;
double s,ans;
void add(int a,int b,int c){
	next[++num]=head[a];
	head[a]=num;
	vet[num]=b;
	cost[num]=c;
}
void change(int l,int r,int x,int p,int vv){
	if (l==r){
		tree[p]+=vv;
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid) change(l,mid,x,p+p,vv);
	else change(mid+1,r,x,p+p+1,vv);
	tree[p]=tree[p+p]+tree[p+p+1];
}
int find(int l,int r,int x,int p){
	if (l==r){
		return(l);
	}
	int mid=(l+r)/2;
	if (tree[p+p]>=x) return(find(l,mid,x,p+p));
	else return(find(mid+1,r,x-tree[p+p],p+p+1));
}
void dfs(int u,int fa,int len){
	if ((len%2)==1){k++;s+=find(1,1000000,(len+1)/2,1);}
	for (int e=head[u];e;e=next[e]){
		int v=vet[e];
		if (v!=fa){
			change(1,1000000,cost[e],1,1);
			dfs(v,u,len+1);
			change(1,1000000,cost[e],1,-1);
		}
	}
}
int main(){
	freopen("random.in","r",stdin);
	freopen("random.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<n;i++){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	for (int i=1;i<=n;i++){
		dfs(i,0,0);
	}
	ans=(double)(s/k);
	printf("%.7lf\n",ans);
}

T3

看着数据就感觉应该要转换成离线(否则不是人能做的)

没错正解就是转成离线→_→

排序后按边权从大到小加边,如果该边的两点已经连通,则删去两点间最长的边。

对每个前缀开可持久化线段树维护

询问[l,r]的答案为编号为l的数的前缀r。

 

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
#define N 1000010
#define M 1000100
struct node{int l,r,s;}T[6000000];
int f[N],a[N],b[N],c[N],root[N],head[N],next[M],cost[M],vet[M],map[M],tid[M];
int num,n,m,k,l,t,s,ans,q,r,f1,f2,id,shit,x,y;
void add(int a,int b,int c,int d){
    next[++num]=head[a];
    head[a]=num;
    vet[num]=b;
    cost[num]=c;
    tid[num]=d;
    next[++num]=head[b];
    head[b]=num;
    vet[num]=a;
    cost[num]=c;
    tid[num]=d;
}
void sort(int l,int r){
    int i=l;
    int j=r;
    int x=c[(l+r)/2];
    while (i<j){
        while (c[i]<x) i++;
        while (x<c[j]) j--;
        if (i<=j){
            t=a[i];a[i]=a[j];a[j]=t;
            t=b[i];b[i]=b[j];b[j]=t;
            t=c[i];c[i]=c[j];c[j]=t;
            i++;
            j--;
        }
    }
    if (l<j) sort(l,j);
    if (i<r) sort(i,r);
}
int find(int x){
    if (f[x]!=x) f[x]=find(f[x]);
    return(f[x]);
}
void ins(int &i,int l,int r,int x,int v){
    T[++id]=T[i];i=id;T[i].s+=v;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) ins(T[i].l,l,mid,x,v);
    else ins(T[i].r,mid+1,r,x,v);
}
int query(int i,int l,int r,int x){
    if ((1<=x)&&(x>=r)) return(T[i].s);
    int mid=(l+r)>>1;
    if (x<=mid) return(query(T[i].l,l,mid,x));
    else return(T[T[i].l].s+query(T[i].r,mid+1,r,x));
}
int dfs(int u,int fa,int st,int l){
    if (u==st){
        t=l;
        return(1);
    }
    int p=l;
    for (int e=head[u];e;e=next[e]){
        int v=vet[e];
        if (v!=fa){
            p=l;
            if ((c[map[l]]<c[map[tid[e]]])||(l==-1)) p=tid[e];
            if (dfs(v,u,st,p)) return(1);
        }
    }
    return(0);
}
int main(){
    scanf("%d%d",&n,&m);
    id=0;
    for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
    sort(1,m);
    for (int i=1;i<=n;i++) f[i]=i;
    for (int w=m;w>0;w--){
        root[w]=root[w+1];
        if (a[w]==b[w]) continue;
        f1=find(a[w]);
        f2=find(b[w]);
        if (f1!=f2){
            f[f2]=f1;
            map[++map[0]]=w;
            ins(root[w],1,m,w,c[w]);
        }else{
            int mx=0;
            for (int i=1;i<=n;i++) head[i]=0; 
            num=0;
            for (int i=1;i<=map[0];i++) add(a[map[i]],b[map[i]],c[map[i]],i);
            dfs(a[w],0,b[w],-1);
//          printf("-%d\n",map[t]);
            ins(root[w],1,m,map[t],-c[map[t]]);
            for (int i=t;i<map[0];i++) map[i]=map[i+1];
            map[map[0]]=w;
            ins(root[w],1,m,w,c[w]);
        }
    }
//  return 0;
    int ans=0;
    scanf("%d%d",&q,&shit);
    for (int i=1;i<=q;i++){
        scanf("%d%d",&x,&y);
        l=ans*shit+x;
        r=l+y;
        l=lower_bound(c+1,c+1+m,l)-c;
        r=upper_bound(c+1,c+1+m,r)-c-1;
        if (l>r) ans=0;
        else ans=query(root[l],1,m,r);
        printf("%d\n",ans);
    }
}
Category: 未分类 | Tags: | Read Count: 341

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com