标签:printf sed 转移 splay define 指针 name pen size
考试打了个记忆化的暴搜,无线接近正解,然而没有想到换一种$dp$方式储存,去优化自己对于结果的优化,实际上稍微改一下就可以了
设$dp[i][j]$代表在第$i$个点用了$j$的时间所能经过的最多景点数,就在$dfs$中放一个$dp$转移就可以了,这种存$dp$值的方法也可以理解为记忆化搜索,记忆化是一个保证$dfs$时间复杂度的好途径
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define maxv 2010 5 #define maxm 510 6 #define maxn 510 7 #define maxe 5010 8 #define inf 4557430888798830399 9 #define int long long 10 using namespace std; 11 int v,m,n,e,l,r,c,ww; 12 int js,ans; 13 int ru[maxv],a[maxm],chu[maxv],b[maxn]; 14 int head[maxv],to[maxe],xia[maxe],w[maxe]; 15 int visit[maxv]; 16 int dp[maxv][maxv]; 17 void add(int x,int y,int z) 18 { 19 to[++js]=y; xia[js]=head[x]; w[js]=z; head[x]=js; 20 } 21 void dfs(int x) 22 { 23 visit[x]=1; 24 for(int i=head[x];i;i=xia[i]) 25 { 26 int ls=to[i]; 27 if(!visit[ls]) dfs(to[i]); 28 for(int j=1;j<=v;++j) 29 { 30 if(dp[ls][j]<inf) dp[x][j+1]=min(dp[x][j+1],dp[ls][j]+w[i]); 31 if(ru[x]&&dp[x][j+1]+a[ru[x]]<=l) ans=max(ans,j+1); 32 } 33 } 34 if(chu[x]) dp[x][1]=min(dp[x][1],b[chu[x]]); 35 } 36 main() 37 { 38 scanf("%lld%lld%lld%lld%lld",&v,&m,&n,&e,&l); memset(dp,0x3f,sizeof(dp)); 39 for(int i=1;i<=m;++i) {scanf("%lld",&r); scanf("%lld",&a[i]); ru[r]=i;} 40 for(int i=1;i<=n;++i) {scanf("%lld",&c); scanf("%lld",&b[i]); chu[c]=i;} 41 for(int i=1;i<=e;++i) {scanf("%lld%lld%lld",&r,&c,&ww); add(r,c,ww);} 42 for(int i=1;i<=v;++i) 43 { 44 if(chu[i]&&ru[i]) ans=max(ans,1ll*1); 45 if(!visit[i]) dfs(i); 46 } 47 printf("%lld\n",ans); 48 return 0; 49 }
考场上打了一个$O(n^2)$的暴力,实际上非常好想,我的思路是找到每一个点对应的最近的一个可以满足不乏味的点,那么对于当前点来说,所有在找到的这个点前面的点都不可以,所有在这个点之后的都可以,这样的话我们就可以用$O(n^2)$的预处理,以及$O(n)$的回答水到很肥的部分分,当然了那个$O(n^2)$的预处理可以通过单调指针的优化变成$O(n)$,但由于询问的$O(n)$无法处理,加上$q$就是$O(n^2)$的时间复杂度,所以即使你快了很多,但仍然逃不过$T80$的命运
接下来是正解,我们先来一波疯狂推式子
设$to[i]$就是我刚才说的那个使以$i$为左端点的最小的不乏味区间的右端点
设$tot[i]$是1到i的和,等差公式,干就完了
然后进入正题
$ans=\sum\limits_{i=l}^{r}[to[i]{\leq}r]\sum\limits_{j=to[i]}^{r}(j-i)$
$=\sum\limits_{i=l}^{r}[to[i]{\leq}r](-i{\times}(r-to[i]+1)+\sum\limits_{j=to[i]}^{r}j$
$=\sum\limits_{i=l}^{r}[to[i]{\leq}r](to[i]{\times}i-i{\times}(r+1)+tot[r]-tot[to[i]-1])$
$=\sum\limits_{i=l}^{r}[to[i]{\leq}r]to[i]{\times}i-(r+1){\times}\sum\limits_{i=l}^{r}[to[i]{\leq}r]i+tot[r]{\times}\sum\limits_{i=l}^{r}[to[i]{\leq}r]-\sum\limits_{i=l}^{r}[to[i]{\leq}r]tot[to[i]-1]$
这样看来的话我们是可以树状数组维护的,那么怎么处理$\sum\limits_{i=l}^{r}[to[i]{\leq}r]$这个条件的,很棒的一个方法,在树状数组中以$to[i]$为下标进行插入,开四个树状数组,一直查询就好了,那么当前我们只满足了$to[i]{\leq}r$,怎么满足$i{\geq}l$这个条件的?在树状数组中不停的清理不符合条件的$i$就可以了
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #define int long long 6 #define maxn 100100 7 using namespace std; 8 struct node{ 9 int l,r,bh; 10 }Q[maxn]; 11 int n,m,q,tot,head=1,head1=1; 12 int a[maxn],to[maxn],kind[maxn]; 13 int c1[maxn],c2[maxn],c3[maxn],c4[maxn],ans[maxn],pd[maxn]; 14 bool cmp(const node &a,const node &b) 15 { 16 return a.l<b.l; 17 } 18 int lowbit(int x) 19 { 20 return x&(-x); 21 } 22 void add(int pos,int w,int a[]) 23 { 24 for(;pos<=n;pos+=lowbit(pos)) a[pos]+=w; 25 } 26 int query(int pos,int a[]) 27 { 28 int ans=0; 29 for(;pos>0;pos-=lowbit(pos)) ans+=a[pos]; 30 return ans; 31 } 32 main() 33 { 34 scanf("%lld%lld%lld",&n,&m,&q); 35 for(int i=1;i<=n;++i) scanf("%lld",&a[i]); 36 for(int i=1;i<=q;++i) {scanf("%lld%lld",&Q[i].l,&Q[i].r); Q[i].bh=i;} 37 for(int i=1;i<=n;++i) 38 { 39 if(tot<m) 40 { 41 if(!kind[a[i]]) tot++; 42 kind[a[i]]++; 43 } 44 while(tot==m) 45 { 46 to[head]=i; kind[a[head]]--; 47 if(!kind[a[head]]) tot--; 48 head++; 49 } 50 } 51 sort(Q+1,Q+q+1,cmp); head=1; 52 for(int i=1;i<=q;++i) 53 { 54 while(to[head]<=Q[i].r&&head<=n) 55 { 56 if(to[head]==0) {head++; continue;} 57 if(head<Q[i].l) {head++; continue;} 58 add(to[head],to[head]*head,c1); add(to[head],head,c2); 59 add(to[head],1,c3); add(to[head],to[head]*(to[head]-1)/2,c4); 60 pd[head]=1; head++; 61 } 62 while(head1<Q[i].l&&head1<=n) 63 { 64 if(!pd[head1]) {head1++; continue;} 65 add(to[head1],-to[head1]*head1,c1); add(to[head1],-head1,c2); 66 add(to[head1],-1,c3); add(to[head1],-to[head1]*(to[head1]-1)/2,c4); 67 head1++; 68 } 69 int ls1=query(Q[i].r,c1)-query(max(Q[i].l,to[Q[i].l])-1,c1); 70 int ls2=query(Q[i].r,c2)-query(max(Q[i].l,to[Q[i].l])-1,c2); 71 int ls3=query(Q[i].r,c3)-query(max(Q[i].l,to[Q[i].l])-1,c3); 72 int ls4=query(Q[i].r,c4)-query(max(Q[i].l,to[Q[i].l])-1,c4); 73 ls2=ls2*(Q[i].r+1); ls3=ls3*Q[i].r*(Q[i].r+1)/2; ans[Q[i].bh]=ls1-ls2+ls3-ls4; 74 } 75 for(int i=1;i<=q;++i) printf("%lld\n",ans[i]); 76 return 0; 77 }
又是期望,成功咕咕咕
标签:printf sed 转移 splay define 指针 name pen size
原文地址:https://www.cnblogs.com/hzjuruo/p/11518788.html