【hackerrank】Week of Code 30
Candy Replenishing Robot
Find the Minimum Number
直接模拟
Melodious password
dfs输出方案
Poles
题意:有多个仓库,只能从后面的仓库运动前面的仓库,现在要把仓库合并成k个,移动一个仓库i到另个仓库j的代价是costi*(xi-xj),问最小代价。
解一下就会发现是个斜率优化,做k次就可以了
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxn 100101 #define LL long long #define rep(i,l,r) for(int i=l;i<=r;i++) #define dow(i,l,r) for(int i=r;i>=l;i--) using namespace std; LL sumd[maxn],sumw[maxn],f[2][maxn],d[maxn],w[maxn]; int n,m,p[maxn]; LL calc(int x,int y) { return f[y][x]-sumd[x]; } int main() { scanf("%d %d",&n,&m); dow(i,1,n) scanf("%lld %lld",&d[i],&w[i]); rep(i,1,n) { sumw[i]=sumw[i-1]+w[i]; sumd[i]=sumd[i-1]+d[i]*w[i]; } // rep(i,1,n) printf("%d %lld %lld %lld\n",i,d[i],sumw[i],sumd[i]);printf("\n"); int now=0; rep(i,1,n) f[0][i]=-(sumw[i]-sumw[0])*d[i]+sumd[i]-sumd[0]; // rep(i,1,n) printf("%lld ",f[0][i]);printf("\n"); rep(i,2,m) { now=1-now; rep(j,1,n) f[now][j]=0; int head,tail; head=tail=1; p[1]=i-1; rep(j,i,n) { // printf("%d %d\n",head,tail); // rep(k,head,tail) printf("%d ",p[k]);printf("\n"); while (head<tail && d[j]*(sumw[p[head+1]]-sumw[p[head]])<calc(p[head],1-now)-calc(p[head+1],1-now)) ++head; f[now][j]=(sumw[p[head]]-sumw[j])*d[j]+(sumd[j]-sumd[p[head]])+f[1-now][p[head]]; // printf("%lld %lld\n",calc(p[tail],1-now),calc(j,1-now)); while (head<tail && (calc(p[tail],1-now)-calc(j,1-now))*(sumw[p[tail]]-sumw[p[tail-1]])>(calc(p[tail-1],1-now)-calc(p[tail],1-now))*(sumw[j]-sumw[p[tail]])) --tail; p[++tail]=j; } // rep(j,1,n) printf("%lld ",f[now][j]);printf("\n"); } printf("%lld\n",f[now][n]); return 0; }
Range Modular Queries
给一个数组,每次查询[l,r]中取模x为y的个数。
我使用莫队直接写的,复杂度比较迷,其实可以预处理出模数为[1,10]的情况再莫队
#include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #define maxn 50000 #define LL long long #define rep(i,l,r) for(int i=l;i<=r;i++) using namespace std; typedef struct { int l,r,k,id,x,y; }Que; Que que[maxn]; int cmp(Que x,Que y) { if (x.k!=y.k) return x.k<y.k; return x.r<y.r; } int num[maxn],c[maxn],n,m,ans[maxn]; int main() { scanf("%d %d",&n,&m); rep(i,0,n-1) scanf("%d",num+i); int len=sqrt(n); rep(i,0,m-1) { scanf("%d %d %d %d",&que[i].l,&que[i].r,&que[i].x,&que[i].y); que[i].id=i; que[i].k=que[i].l/len; } sort(que,que+m,cmp); int i=0; while (i<m) { // printf("%d\n",i); int k=que[i].k; memset(c,0,sizeof(c)); rep(j,que[i].l,que[i].r) ++c[num[j]]; int now; now=que[i].y; while (now<maxn) ans[que[i].id]+=c[now],now+=que[i].x; // printf("!!\n"); ++i; while (que[i].k==k&&i<m) { // printf("!!\n"); rep(j,que[i].l,que[i-1].l-1) c[num[j]]++; rep(j,que[i-1].l,que[i].l-1) c[num[j]]--; rep(j,que[i-1].r+1,que[i].r) c[num[j]]++; rep(j,que[i].r+1,que[i-1].r) c[num[j]]--; now=que[i].y; while (now<maxn) ans[que[i].id]+=c[now],now+=que[i].x; ++i; } // printf("!!!\n"); } rep(i,0,m-1) printf("%d\n",ans[i]); return 0; }
标解的方法显然更好,
分两部分处理,1-k和k到n(k=sqrt(n));
对于第一部分,建立一个pos[i][j]的vector表示模数为i,结果为j的数的下标,每次查询就直接二分找出下标,相减就是个数。
对于第二部分,建立一个poss[i]的vector表示数为i的下标,对于每次询问从x开始,每次查询l,r区间有多少个这个数,一样是二分查找下标相减
A Graph Problem
题意:给一个图,让你选出其中一些点,使得这些点中边组成的三角形/点的个数最大。
求比值最大显然是01分数规划,化简一下就是y-g*x=0,这是一个最大权闭合图,用网络流建图解决。记住收益=正的收益-最小割(最大流),收益>0就是存在方案。不过最后要输出方案我gg了,一个是精度,另一个是最后要用l重新跑一遍,然后找出和s不在一个集合的点,就是从s开始dfs找不到的点,
#include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #define LL long long #define maxn 100000 #define maxm 100000 #define rep(i,l,r) for(int i=l;i<=r;i++) #define dow(i,l,r) for(int i=r;i>=l;i--) #define repedge(i,x) for(int i=cur[x];i>=0;i=e[i].next) #define repedge2(i,x) for(int i=cur[x];i>=0;i=e[i].next) #define inf 10000 using namespace std; typedef struct { int toward,next; double cap; } Edge; Edge e[maxm]; int d[maxn],chose[maxn],gap[maxn],fi[maxn],cur[maxn],n,m,tot1,total,tar[maxn][3],ap[100][100],s,t; double esp=0.000000001; void add(int j,int k,double l) { e[total].toward=k; e[total].next=fi[j]; fi[j]=total; e[total].cap=l; ++total; } void addedge(int j,int k,double l) { add(j,k,l); add(k,j,0); } double sap(int x,double flow) { // printf("%d %lf\n",x,flow); if (x==t) return flow; double now=0; repedge(i,x) { int too=e[i].toward; if (d[x]==d[too]+1 && e[i].cap>0) { double more=sap(too,min(e[i].cap,flow-now)); e[i].cap-=more; e[i^1].cap+=more; cur[x]=i; if (flow==(now+=more)) return flow; } } if (!(--gap[d[x]])) d[s]=t; gap[++d[x]]++; cur[x]=fi[x]; return now; } double maxflow() { rep(i,1,t) d[i]=0,gap[i]=0,cur[i]=fi[i]; gap[0]=t; double sum=0; while (d[s]<t) sum+=sap(s,inf); return sum; } void dfs(int x) { // printf("%d\n",x); chose[x]=1; repedge2(i,x) { int too=e[i].toward; // printf("\t%d %lf\n",too,e[i].cap); if (e[i].cap>esp && !chose[too]) dfs(too); } } int main() { scanf("%d",&n); rep(i,1,n) rep(j,1,n) scanf("%d",&ap[i][j]); tot1=0; rep(i,1,n) rep(j,i+1,n) rep(k,j+1,n) if (ap[i][j] && ap[i][k] && ap[j][k]) { ++tot1; tar[tot1][0]=i; tar[tot1][1]=j; tar[tot1][2]=k; } s=n+tot1+1;t=n+tot1+2; double l=0,r=1000; while (r-l>=esp) { double mid=(l+r)/2.0; // printf("%lf\n",mid); total=0; rep(i,1,t) fi[i]=-1; rep(i,1,tot1) rep(j,0,2) addedge(tar[i][j],i+n,inf); rep(i,1,n) addedge(s,i,mid); rep(i,1,tot1) addedge(i+n,t,1); if (tot1-maxflow()>0) l=mid; else r=mid; } // printf("%lf %lf %lf\n",l,r,(l+r)/2); total=0; rep(i,1,t) fi[i]=-1; rep(i,1,tot1) rep(j,0,2) addedge(tar[i][j],i+n,inf); rep(i,1,n) addedge(s,i,l); rep(i,1,tot1) addedge(i+n,t,1); maxflow(); memset(chose,0,sizeof(chose)); dfs(s); int tot2=0; rep(i,1,n) if (!chose[i]) ++tot2; printf("%d\n",tot2); rep(i,1,n) if (!chose[i]) printf("%d ",i); printf("\n"); return 0; }
Substring Queries
给多个字符串,求出其中某两个字符串的lcp。
建立一个广义后缀数组。然后记录每个字符串的后缀在sa[]中的位置,对于一个询问,s1,s2,取出s1的每一个后缀和s2的每一个后缀算一个lcp,可以发现只会和s2中最接近这个后缀的两个后缀有关系,也就是每次都是单调的,一次询问就是s1+s2的后悔次数,然后做两个小优化,一个是把询问一样的一起处理,一个是每次s1选取那个短一点的,最后摊下来是一个比较好看的复杂度。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> #define maxn 210000 #define maxm 210000 #define rep(i,l,r) for(int i=l;i<=r;i++) #define dow(i,l,r) for(int i=r;i>=l;i--) using namespace std; vector<int> vec[maxn]; int len,n,f[20][maxn],tot,sa[maxn],ran[maxn],h[maxn],num[maxn],d[maxn],ans[maxn],x[maxn],y[maxn],c[maxn],m,size[maxn],col[maxn]; char s[maxn],st[maxn]; typedef struct { int l,r,id; }Que; Que que[maxn]; int cmp(Que a,Que b) { if (a.r!=b.r) return a.r<b.r; return a.l<b.l; } void makesa() { for(int p=1;p<len;p*=2) { // printf("%d\n",p); rep(i,1,p) y[i]=len-p+i; int j=p; rep(i,1,len) if (sa[i]>p) y[++j]=sa[i]-p; rep(i,1,len) x[i]=ran[y[i]]; memset(c,0,sizeof(c)); rep(i,1,len) c[x[i]]++; rep(i,1,tot) c[i]+=c[i-1]; dow(i,1,len) sa[c[x[i]]--]=y[i]; x[sa[tot=1]]=1; rep(i,2,len) { tot+=(ran[sa[i]]!=ran[sa[i-1]] || ran[sa[i]+p]!=ran[sa[i-1]+p]); x[sa[i]]=tot; } rep(i,1,len) ran[i]=x[i]; // rep(i,1,len) printf("%s\n",&s[sa[i]]); if (tot==len) break; } rep(i,1,len) sa[ran[i]]=i; } void makeheight() { h[1]=0; int last=0; rep(i,1,len) { last=max(last-1,0); if (ran[i]==1) continue; int j=sa[ran[i]-1]; while (s[i+last]==s[j+last]) ++last; h[ran[i]]=last; } } void into() { scanf("%d %d",&n,&m); len=0; rep(i,1,n) { scanf("%s",st); size[i]=strlen(st); s[++len]='#'; rep(j,0,size[i]-1) { s[++len]=st[j]; d[len]=size[i]-j; col[len]=i; } } s[++len]='$'; memset(c,0,sizeof(c)); rep(i,1,len) c[x[i]=s[i]]++; rep(i,1,128) c[i]+=c[i-1]; dow(i,1,len) sa[c[x[i]]--]=i; ran[sa[tot=1]]=1; rep(i,2,len) ran[sa[i]]=(tot+=(x[sa[i]]!=x[sa[i-1]])); // printf("%d\n",tot); // rep(i,1,len) printf("%s\n",&s[sa[i]]); makesa(); // printf("!!!\n"); makeheight(); // printf("!!!\n"); // rep(i,1,len) printf("%3c",s[i]);printf("\n"); // rep(i,1,len) printf("%3d",sa[i]);printf("\n"); // rep(i,1,len) printf("%3d",rank[i]);printf("\n"); // rep(i,1,len) printf("%3d",h[i]);printf("\n"); // rep(i,1,len) printf("%3d",col[i]);printf("\n"); // rep(i,1,len) printf("%s\n",&s[sa[i]]); rep(i,2,len) h[i]=min(h[i],d[sa[i]]); rep(i,1,len) { int j=col[sa[i]]; // printf("%d %d\n",j,i); if (j) vec[j].push_back(i); } // rep(i,1,n) { // printf("%d:%d",i,vec[i].size()); // rep(j,0,vec[i].size()-1) printf("%3d",vec[i][j]); // printf("\n"); // } rep(i,1,len) f[0][i]=h[i]; rep(i,1,19) rep(j,1,len-(1<<i)+1) f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]); // rep(i,0,16) // rep(j,1,len-(1<<i)+1) // printf("%d %d %d %d\n",i,j,j-1+(1<<i),f[i][j]); // printf("!!\n"); rep(i,0,len) num[i]=(int)floor(log(i)/log(2)); } int calc(int l,int r) { // printf("%d %d\n",l,r); if (l>r) swap(l,r); l++; int i=num[r-l+1]; return min(f[i][l],f[i][r+1-(1<<i)]); } void ask() { rep(i,0,m-1) { scanf("%d %d",&que[i].l,&que[i].r); ++que[i].r; ++que[i].l; if (size[que[i].l]<size[que[i].r]) swap(que[i].l,que[i].r); que[i].id=i; } sort(que,que+m,cmp); // printf("!!\n"); // rep(i,0,m-1) printf("%d %d %d\n",que[i].l,que[i].r,que[i].id); rep(i,0,m-1) { if (i && que[i].l==que[i-1].l && que[i].r==que[i-1].r) ans[que[i].id]=ans[que[i-1].id]; else { int now=0,x=que[i].r,y=que[i].l,last=0; rep(j,0,size[x]-1) { while (now+2<size[y] && vec[y][now+1]<vec[x][j]) ++now; last=max(last,calc(vec[x][j],vec[y][now])); if (now<size[y]) last=max(last,calc(vec[x][j],vec[y][now+1])); } ans[que[i].id]=last; } } rep(i,0,m-1) printf("%d\n",ans[i]); } int main() { into(); ask(); return 0; }
说到底还是自己太弱,很怠惰,本来倒数第二题思路是对的一直不敢写,想想这星期除了cf就只有这一点点练习,真是的!