又是一场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); } }