首页 > 代码库 > cf Round 601

cf Round 601

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\n",vis[n]);
    return 0;
}
View Code

 

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\n",ans);
    }
    return 0;
}
View Code

 

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\n",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\n",ans+1);
    return 0;
}
View Code

 

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\n%d\n",ma,ans);
    return 0;
}
View Code

 

E.A Museum Robbery(待填坑)

 

cf Round 601