HDU 5719
题意概括:
第一行输入t表示输入数据,每组数据第一行n,表示对1—n进行排序。接下来输入n个数b[n]表示排列中第i个数之前的最小值为b[i]。第三行n个数c[n],表示排列中第i个数之前的最大值为c[i]。
解题思路:
递推,排除掉6种不可能的情况,1、b[i]>b[i-1] 2、c[i]<c[i-1] 3、b[i]>c[i] 4、c[1]!=b[1] 5、b[i],c[i] < 1 || > n 6、c[i]>c[i-1] &&b[i]<b[i-1]两个条件同时满足时。然后递推,如果当前产生的新的 b[i]或者 c[i] 那么dp[i] = dp[i-1] ,如果当前 b[i-1] = b[i] && c[i-1] = c[i] ,那么我们可以在 [b[i],c[i]]中任选一个数,但是由于谷堆是互不相同的,所以每次我们的选项都会变少,弄个计数器计算一下当前已经选了多少种,减掉之后答案即为 dp[i] = dp[i-1]*(c[i]-b[i]+1-num)
官方题解:
具体实现
考虑使用数组存储(当然直接用一个ans从头到尾递推也是可以的)
#include<bits/stdc++.h> using namespace std; const int N=1e6+5,mod=998244353; typedef long long LL; typedef unsigned long long ULL; int b[N],c[N],n; LL dp[N]; int main() { //freopen("perm.in","r",stdin); //freopen("perm.out","w",stdout); int T; cin>>T; while(T--) { memset(dp,0,sizeof(dp)); int flag=1; cin>>n; cin>>b[1]; for(int i=2;i<=n;i++) { scanf("%d",&b[i]); if(b[i]>b[i-1]||b[i]<1||b[i]>n) flag=0; } cin>>c[1]; for(int i=2;i<=n;i++) { scanf("%d",&c[i]); if(c[i]<c[i-1]||c[i]<1||c[i]>n||c[i]<b[i]) flag=0; if(c[i]>c[i-1]&&b[i]<b[i-1]) flag=0; } if(b[1]!=c[1]) flag=0; if(flag==0) { cout<<0<<endl; continue; } else { int num=1; dp[1]=1; for(int i=2;i<=n;i++) { if(b[i]==b[i-1]&&c[i]==c[i-1]) dp[i]=dp[i-1]*(c[i]-b[i]+1-num)%mod; else if((b[i]==b[i-1]&&c[i]>c[i-1])||(b[i]<b[i-1]&&c[i]==c[i-1])) dp[i]=dp[i-1]; num++; } printf("%lld\n",dp[n]); } } return 0; }
HDU 5807
解题思路
直接枚举当前三个人的状态以及下一步状态时间复杂度O(n^6),考虑优化,本来是三个人同时走向下一个城市,现在给这三个同步的操作定一个顺序,那么每个合法任务应该是007,008,009,007,008,009,……,007,008,009
dp[i][j][k][0]表示当前三人分别在i,j,k城市,且下一步该i走的方法数;
dp[i][j][k][1]表示当前三人分别在i,j,k城市,且下一步该j走的方法数;
dp[i][j][k][2]表示当前三人分别在i,j,k城市,且下一步该k走的方法数;
那么dp[i][j][k][0]的后继状态就是dp[ii][j][k][1](ii为i的出边对应的另一端点),dp[i][j][k][1]的后继状态就是dp[i][jj][k][2](jj为j的出边对应的另一端点),dp[i][j][k][2]的后继状态就是dp[i][j][kk][0](kk为k的出边对应的另一端点),进而我们就可以由dp[0]->dp[1],dp[1]->dp[2],dp[2]->dp[0]得到所有状态的方法数,初始化的时候如果i,j,k满足条件则令dp[i][j][k][0]=1,否则令dp[i][j][k]=0,注意最后由dp[2]->dp[0]的时候需要判断这个dp[i][j][k][0]是否满足条件,求出dp数组后对于每次查询,如果三个特工初始位置是a,b,c的话,那么答案就是dp[a][b][c][0]
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=55,mod=998244353; int T,n,m,K,q,w[N]; ll dp[N][N][N][5]; vector<int>G[N]; void dfs() { for(int i=n;i>=1;--i) for(int j=n;j>=1;--j) for(int k=n;k>=1;--k) { dp[i][j][k][0]=1; for(auto x:G[i]) dp[i][j][k][0]=(dp[x][j][k][2]+dp[i][j][k][0])%mod; for(auto x:G[j]) dp[i][j][k][1]=(dp[i][x][k][0]+dp[i][j][k][1])%mod; for(auto x:G[k]) dp[i][j][k][2]=(dp[i][j][x][1]+dp[i][j][k][2])%mod; if(abs(w[i]-w[j])>K||abs(w[i]-w[k])>K||abs(w[k]-w[j])>K) dp[i][j][k][0]=0; } } int main() { //freopen("task.in","r",stdin); //freopen("task.out","w",stdout); cin>>T; while(T--) { scanf("%d%d%d%d",&n,&m,&K,&q); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=0;i<N;i++) G[i].clear(); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); } memset(dp,0,sizeof(dp)); dfs(); for(int i=1;i<=q;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); printf("%lld\n",dp[a][b][c][0]); } } return 0; }
HDU 5597
证明
证明出自Sept
链接 2023.07.16 高质量 NOIP 模拟赛题解 - CSP_Sept - 博客园 (cnblogs.com)
#include<bits/stdc++.h> using namespace std; #define ll __int64 int main() { ll n, x; while(~scanf("%I64d%I64d", &n, &x)) { x = n+x+1; ll ans=x; for(ll i=2; i*i<=x; i++) { if(x%i==0) { ans=ans/i*(i-1); while(x%i==0) x/=i; } } if(x>1) ans=ans/x*(x-1); printf("%I64d\n", ans); } return 0; }
HDU 5669
解题思路:
分析:(官方题解)
首先考虑暴力,显然可以直接每次O(n^2)
的连边,最后跑一次分层图最短路就行了.
然后我们考虑优化一下这个连边的过程 ,因为都是区间上的操作,所以能够很明显的想到利用线段树来维护整个图,
连边时候找到对应区间,把线段树的节点之间连边.这样可以大大缩减边的规模,然后再跑分层图最短路就可以了.
但是这样建图,每一次加边都要在O(logn)个线段树节点上加边,虽然跑的非常快,但是复杂度仍然是不科学的.
为了解决边的规模的问题,开两棵线段树,连边时候可以新建一个中间节点,在对应区间的节点和中间节点之间连边
进一步缩减了边的规模,强行下降一个数量级
最后跑一下分层图最短路就行了
复杂度O(mlog^2n)
什么你会线段树但是不会分层图最短路?安利JLOI2011飞行路线.
因为边的数目还是相对比较多的,所以不能使用SPFA,而要使用Heap-Dijkstra来做最短路,
但是不排除某些厉害的选手有特殊的SPFA姿势可以做或者普通 SPFA写的比较优美就不小心跑过去了...
注:出题人的题解写的很详细了,然后JLOI2011飞行路线是BZOJ2763 直接去做就好了
然后我的建图刚开始不太完善,跑了600+ms,然后后来完善了一下,按线段树节点建图(这就是题解)
不过每个线段树的节点不需要和它的区域内所有的点连边,只需要按照线段树的结构,连它的左右儿子就行了
#include <cstdio> #include <map> #include <algorithm> #include <vector> #include <iostream> #include <set> #include <queue> #include <string> #include <cstdlib> #include <cstring> #include <cmath> using namespace std; typedef pair<int,int>pii; typedef long long LL; const int N=6e5+5; const int mod=1e9+7; int n,m,s,k,t,cnt,idl[N<<1],idr[N<<1]; bool vis[N][11]; LL d[N][11]; vector<pii>edg[N]; void buildl(int rt,int l,int r) { idl[rt]=++cnt; if(l==r)return ; int m=l+r>>1; buildl(rt<<1,l,m); buildl(rt<<1|1,m+1,r); edg[idl[rt<<1]].push_back(make_pair(idl[rt],0)); edg[idl[rt<<1|1]].push_back(make_pair(idl[rt],0)); } void buildr(int rt,int l,int r) { idr[rt]=++cnt; if(l==r)return ; int m=l+r>>1; buildr(rt<<1,l,m); buildr(rt<<1|1,m+1,r); edg[idr[rt]].push_back(make_pair(idr[rt<<1],0)); edg[idr[rt]].push_back(make_pair(idr[rt<<1|1],0)); } void pre(int rt,int l,int r) { if(l==r) { edg[l].push_back(make_pair(idl[rt],0)); edg[idr[rt]].push_back(make_pair(l,0)); return; } int m=l+r>>1; pre(rt<<1,l,m); pre(rt<<1|1,m+1,r); } void addl(int rt,int l,int r,int L,int R,int w){ if(L<=l&&r<=R){ edg[idl[rt]].push_back(make_pair(cnt,w)); return; } int mid=(l+r)/2; if(L<=mid)addl(rt*2,l,mid,L,R,w); if(R>mid)addl(rt*2+1,mid+1,r,L,R,w); } void addr(int rt,int l,int r,int L,int R){ if(L<=l&&r<=R){ edg[cnt].push_back(make_pair(idr[rt],0)); return; } int mid=(l+r)/2; if(L<=mid)addr(rt*2,l,mid,L,R); if(R>mid)addr(rt*2+1,mid+1,r,L,R); } struct man{ int v; int c; LL w; bool operator<(const man &e)const{ return w>e.w; } }; priority_queue<man>q; void dij(int s){ memset(d,-1,sizeof d);memset(vis,0,sizeof vis); d[s][0]=0; q.push(man{s,0,0}); while(!q.empty()){ int u=q.top().v,c=q.top().c;q.pop(); if(vis[u][c])continue; vis[u][c]=1; for(int i=0;i<edg[u].size();++i){ int v=edg[u][i].first,w=edg[u][i].second; if(!vis[v][c]&&(d[v][c]==-1||d[v][c]>d[u][c]+w)){ d[v][c]=d[u][c]+w; q.push(man{v,c,d[v][c]}); } if(c<k){ if(!vis[v][c+1]&&(d[v][c+1]==-1||d[v][c+1]>d[u][c])){ d[v][c+1]=d[u][c]; q.push(man{v,c+1,d[v][c+1]}); } } } } } int main() { int x,y,w,l,r; int T; scanf("%d",&T); while(T--){ for(int i=0;i<N;i++)edg[i].clear(); scanf("%d%d%d",&n,&m,&k); cnt=n; buildl(1,1,n); buildr(1,1,n); pre(1,1,n); while(m--) { ++cnt; scanf("%d%d%d%d%d",&x,&y,&l,&r,&w); addl(1,1,n,x,y,w); addr(1,1,n,l,r); cnt++; //此处要注意 addl(1,1,n,l,r,w); addr(1,1,n,x,y); } dij(1); LL ans=1000000000000; for(int i=0;i<=k;i++)ans=min(ans,d[n][i]); if(ans!=-1)printf("%lld\n",ans); else puts("CreationAugust is a sb!"); } return 0; }
标签:连边,Noip,int,题解,long,赛口,include,dp From: https://www.cnblogs.com/mingloch/p/17558074.html