A.The Two Routes(BFS)
给出n个城镇,有m条铁路,铁路的补图是公路,汽车和火车同时从1出发,通过每条路的时间为1,不能同时到达除了1和n的其它点,问他们到达n点最少要用多长时间。
因为是补图,那么一定有一条路是可以直接从1到达n的。
那么我们把剩下的用bfs求一下即可。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=405; //Code begin... int n, m, G[N][N], g[N][N], vis[N]; void bfs(int a[][N]) { queue<int>q; q.push(1); vis[1]=0; while (!q.empty()) { if (vis[n]!=-1) break; int u=q.front(); q.pop(); FOR(i,1,n) if (a[u][i]&&vis[i]==-1) q.push(i), vis[i]=vis[u]+1; } } int main () { int u, v; mem(vis,-1); scanf("%d%d",&n,&m); while (m--) scanf("%d%d",&u,&v), G[u][v]=G[v][u]=1; FOR(i,1,n) FOR(j,i+1,n) if (G[i][j]==0) g[i][j]=g[j][i]=1; if (G[1][n]==1) bfs(g); else bfs(G); printf("%d ",vis[n]); return 0; }
B.Lipshitz Sequence(单调队列)
给出一个序列和q个询问。询问区间的所有子区间的lipshitz的总和是多少。
lipshitz是指对于区间[l,r], 最大的ceil(|a[i]-a[j]|/i-j).(l<=j<i<=r)。
数形结合一下,我们画下图可以发现,一个区间[l,r]内的lipshitz一定是由两个相邻的数产生的。
于是我们把|a[i+1]-a[i]|预处理出来,计算每个值能对结果产生的贡献。
于是我们需要知道这个值它最左能延伸多少,最右能延伸多少。
用两个单调队列扫两下就行了。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=100005; //Code begin... int a[N], b[N], q[N], l[N], r[N], head, tail; int main () { int n, qq, c, d; scanf("%d%d",&n,&qq); FOR(i,1,n) scanf("%d",a+i); FO(i,1,n) b[i]=abs(a[i+1]-a[i]); head=1; tail=0; FO(i,1,n) { while (head<=tail&&b[q[tail]]<=b[i]) tail--; l[i]=q[tail]; q[++tail]=i; } head=1; tail=0; q[tail]=n; for (int i=n-1; i>=1; --i) { while (head<=tail&&b[q[tail]]<b[i]) tail--; r[i]=q[tail]; q[++tail]=i; } while (qq--) { scanf("%d%d",&c,&d); LL ans=0; FO(i,c,d) { int l1=max(c,l[i]+1), r1=min(d-1,r[i]-1); ans+=(LL)b[i]*(i-l1+1)*(r1-i+1); } printf("%lld ",ans); } return 0; }
C.Kleofáš and the n-thlon(概率DP)
有n场比赛,和m名参赛者,已知这n场比赛每个人的rank都不同。
给出你每场比赛的排名,求最终排名的期望,最终排名是比你rank之和低的人数+1.
明显的DP,令dp[i][j]表示第i场比赛时分数为j的时候人数的期望。
那么有dp[i][j]=sum(dp[i-1][k])/(m-1).(j-m<=k<=j-1&&k!=j-a[i]).
这个复杂度是O(n*m*m)。
观察发现转移的时候加的是一个区间。我们用前缀和优化转移。
复杂度O(n*m).
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=100005; //Code begin... int a[105], flag; double dp[2][100005]; int main () { int n, m, sum=0; scanf("%d%d",&n,&m); FOR(i,1,n) scanf("%d",a+i), sum+=a[i]; if (m==1) {printf("%.10lf ",1.0); return 0;} dp[0][0]=m-1; FOR(i,1,n) { flag^=1; mem(dp[flag],0); FOR(j,i,i*m) { int l=max(j-m,i-1), r=min(j-1,(i-1)*m); dp[flag][j]=dp[flag^1][r]-(l==0?0:dp[flag^1][l-1]); int k=j-a[i]; if (k>=l&&k<=r) dp[flag][j]-=(dp[flag^1][k]-(k==0?0:dp[flag^1][k-1])); dp[flag][j]/=(m-1); } FOR(j,i,i*m) dp[flag][j]+=dp[flag][j-1]; } double ans=(dp[flag][sum-1]-dp[flag][n-1]); printf("%.10lf ",ans+1); return 0; }
D.Acyclic Organic Compounds(Trie树合并)
我们算出每个节点的dif,这需要用到trie树的合并操作。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=300005; //Code begin... struct Edge{int p, next;}edge[N<<1]; int head[N], cnt=1, val[N], ch[N*15][27], tot, size[N*15]; char s[N]; void add_edge(int u, int v) { edge[cnt].p=v; edge[cnt].next=head[u]; head[u]=cnt++; } int merge(int u, int v) { if (u<0) return v; if (v<0) return u; int t=++tot; size[t]=1; FOR(i,1,26) { ch[t][i]=merge(ch[u][i], ch[v][i]); if (ch[t][i]>=0) size[t]+=size[ch[t][i]]; } return t; } void dfs(int x, int fa) { FOR(i,1,26) ch[x][i]=-1; for (int i=head[x]; i; i=edge[i].next) { int v=edge[i].p; if (v==fa) continue; dfs(v,x); int lab=s[v]-'a'+1; ch[x][lab]=merge(ch[x][lab],v); } size[x]=1; FOR(i,1,26) if (ch[x][i]>=0) size[x]+=size[ch[x][i]]; val[x]+=size[x]; } int main () { int n, u, v; scanf("%d",&n); FOR(i,1,n) scanf("%d",val+i); scanf("%s",s+1); FO(i,1,n) scanf("%d%d",&u,&v), add_edge(u,v), add_edge(v,u); tot=n; dfs(1,0); int ma=0, ans=0; FOR(i,1,n) ma=max(val[i],ma); FOR(i,1,n) if (ma==val[i]) ans++; printf("%d %d ",ma,ans); return 0; }
E.A Museum Robbery(待填坑)