T1.最短路path
求经过k个必经点的最短路
100%的数据n<=50000,m<=100000,0<=k<=10,1<=z<=5000。
写了naive的算法,类似分层图的状压最短路,然而加成了双向边,炸成10分,感谢良心出题人。
正解考虑k+1个点到终点的最短路,+的1是起点,然后它们之间用状压DP求出彼此最短路,再加上到终点的最短路即为答案。
答案会超过MAX_INT

#include<iostream> #include<cstring> #include<cstdio> #include<queue> #define int long long using namespace std; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } int n,m,num,s,t; int lim[16]; const int MAXN=50005; struct Edge{ int next,to,w; }e[MAXN<<1]; int ecnt,head[MAXN]; inline void add(int x,int y,int w){ e[++ecnt].next = head[x]; e[ecnt].to = y; e[ecnt].w = w; head[x] = ecnt; } struct Node{ int id,w; Node(int x=0,int y=0){id=x;w=y;} bool operator<(const Node &rhs)const { return w>rhs.w; } }top; bool vis[MAXN]; int dis[MAXN][12]; priority_queue<Node> Q; void dij(int st,int cur){ memset(vis,0,sizeof(vis)); while(!Q.empty()) Q.pop(); dis[st][cur]=0;Q.push(Node(st,0)); while(!Q.empty()){ top=Q.top(); Q.pop(); int mn=top.w,mnid=top.id; if(dis[mnid][cur]!=mn) continue; vis[mnid]=1; for(int j=head[mnid];j;j=e[j].next){ int v=e[j].to; if(mn+e[j].w<dis[v][cur]){ dis[v][cur]=mn+e[j].w; Q.push(Node(v,dis[v][cur])); } } } } int f[1<<12][12]; signed main(){ n=rd();m=rd();num=rd();s=rd();t=rd(); int x,y,w; for(int i=1;i<=m;i++){ x=rd();y=rd();w=rd(); add(x,y,w); } for(int i=1;i<=num;i++){ lim[i]=rd(); } memset(dis,0x3f,sizeof(dis)); for(int i=1;i<=num;i++){ dij(lim[i],i); } lim[num+1]=s; dij(s,num+1); memset(f,0x3f,sizeof(f)); for(int i=1;i<=num+1;i++) f[1<<(i-1)][i]=dis[lim[i]][num+1]; for(int i=0;i<(1<<(num+1));i++){ for(int j=1;j<=num+1;j++){ if(!(i&(1<<(j-1)))) continue; for(int k=1;k<=num+1;k++){ if(i&(1<<(k-1))) continue; f[i|(1<<(k-1))][k]=min(f[i|(1<<(k-1))][k],f[i][j]+dis[lim[k]][j]); } } } int ans=1ll<<60; for(int i=1;i<=num+1;i++){ ans=min(ans,f[(1<<(num+1))-1][i]+dis[t][i]); } printf("%lld",ans==1ll<<60?-1:ans); return 0; }
T2.剑与魔法(dragons)
一些前缀限制(前缀不能选超过xi个数),最后一个不限制,还是hsz最巨。
您最强。
这是一个贪心,写了一个O(n^2)DP,还没考虑最后一个不限制..
用小根堆维护选出的数,遇到限制就不断pop直到符合限制,可以证明,最后剩下的就是最优解了。

#include<iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } priority_queue<int> Q; int n; int main(){ n=rd(); char s[50]; for(int i=1;i<=n-1;i++){ scanf("%s",s); if(s[0]=='c') Q.push(-rd()); else{ int lim=rd(); while(Q.size()>=lim) Q.pop(); } } int ans=0; while(!Q.empty()) ans-=Q.top(),Q.pop(); cout<<ans; return 0; }
T3.三角形(triangle)
给出n个点,问可以构成多少个三角形。
求共线点对数量,暴力O(n^3)
可以枚举一个点,计算出其他所有点的斜率,取相同段长为xi,ans-=sigma(C(xi,2))

#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } const int MAXN=3005; int n; int x[MAXN],y[MAXN]; double a[MAXN]; int p; long long ans; int tmp; int main(){ // freopen("triangle.in","r",stdin); // freopen("triangle.out","w",stdout); n=rd(); ans=1ll*n*(n-1)*(n-2)/6; for(int i=1;i<=n;i++) x[i]=rd(),y[i]=rd(); for(int i=1;i<=n;i++){ tmp=0;p=0; for(int j=i+1;j<=n;j++){ if(x[j]-x[i]==0) tmp++; else a[++p]=1.0*(y[j]-y[i])/(x[j]-x[i]); } sort(a+1,a+1+p); a[p+1]=19491001.19491001; int cnt=1; for(int j=2;j<=p+1;j++){ if(a[j]==a[j-1]) cnt++; else{ ans-=1ll*(cnt*(cnt-1)/2); cnt=1; } } ans-=1ll*(tmp*(tmp-1)/2); } cout<<ans; return 0; }