[Atcoder SHPC2018] Tutorial

Link:

SHPC2018 传送门

C:

一道看上去有些吓人的题目,不过$1e9$规模下的$n^m$代表肯定是可以约分

可以发现能提供贡献的数对只有$2*(n-d)$种,那么总贡献为$2*(n-d)*(m-1)*n^{m-2}$

除去$n^m$后就是$\frac{2*(n-d)*(m-1)}{n^2}$($d=0$时要特殊处理)

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> P;
ld n,m,d;
int main()
{
    scanf("%Lf%Lf%Lf",&n,&m,&d);
    printf("%.10Lf",(n-d)*(m-1)/(n*n)*(d?2.0:1.0));
    return 0;
}
Problem C

一般此类难以枚举所有状态的题目都是利用计算每种可能答案的贡献来解决

 

D:

发现了自己英语渣渣的本质……

把$some day$理解为$some days$就觉得不可做了,以后还是要学好英语注意单复数啊……

如果只能转换一次就非常容易了:

分别从起点和终点根据不同边权跑最短路,算出在每个点转换时的最小代价

从后往前取$min$就能找到在前$i$个点不能转换时的最小代价了

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<ll,ll> P;
const int MAXN=1e5+10;
int n,m,S,T,x,y,a,b;
ll res[MAXN],INF=1e15,d1[MAXN],d2[MAXN];
vector<P> G1[MAXN],G2[MAXN];

void dijkastra(vector<P> *G,ll *d,int start)
{
    priority_queue<P,vector<P>,greater<P> > q;
    for(int i=0;i<MAXN;i++) d[i]=INF;
    q.push(P(0,start));d[start]=0;
    while(!q.empty())
    {
        P t=q.top();q.pop();
        int u=t.Y;ll cost=t.X;
        if(cost>d[u]) continue;
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i].X;
            if(d[v]>d[u]+G[u][i].Y)
                d[v]=d[u]+G[u][i].Y,q.push(P(d[v],v));
        }
    }
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&S,&T);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&x,&y,&a,&b),
        G1[x].push_back(P(y,a)),G1[y].push_back(P(x,a)),
        G2[x].push_back(P(y,b)),G2[y].push_back(P(x,b));
    
    dijkastra(G1,d1,S);dijkastra(G2,d2,T);
    for(int i=1;i<=n;i++) res[i]=d1[i]+d2[i];
    for(int i=n-1;i>=1;i--) res[i]=min(res[i],res[i+1]);
    for(int i=1;i<=n;i++) printf("%lld\n",INF-res[i]);
    return 0;
}
Problem D

 

E:

首先要意识到:如果一个点确定,其它所有点都能确定(除非有矛盾)

因此我们只要判断第一个点有多少种取值可行就能算出总的方案数

 

如果点$i$与起点相距偶数条边时$a_i=t+a_1$,相距奇数条边时$a_i=t-a_1$($t$为常数)

又由于要求$a_i$为正整数,我们根据$a_i\ge 0$的约束条件不断缩减$a_1$的范围即可

(需要注意,如果出现环时要么不可行要么确定唯一的$a_1$)

 

实现时只要写一个$dfs$即可,不断推导当前的$t$和$a_1$前的正/负号

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
struct edge{int nxt,to,w;}e[MAXN<<2];
ll l=0,r=1ll<<40,dif[MAXN],fst=0;//有可能爆2e9 
int n,m,x,y,z,head[MAXN],mk[MAXN],vis[MAXN],tot=0;

void add(int from,int to,int w)
{e[++tot].nxt=head[from];e[tot].to=to;e[tot].w=w;head[from]=tot;}

void dfs(int x,ll cur,int mark,int anc)
{
    if(!vis[x])
    {
        mk[x]=mark;dif[x]=cur;vis[x]=1;
        if(fst&&dif[x]+mk[x]*fst<=0)
            puts("0"),exit(0);
        
        if(mk[x]>0) l=max(l,-dif[x]+1);
        else r=min(r,dif[x]-1);
    }
    else
    {
        if(cur==dif[x]&&mark==mk[x]) return;
        if(cur!=dif[x]&&mark==mk[x])
            puts("0"),exit(0);
        
        ll t=(dif[x]-cur)/(mark-mk[x]);
        if(t*(mark-mk[x])==dif[x]-cur && ((!fst)||fst==t)) fst=t;
        else puts("0"),exit(0);
        return;
    }
    for(int i=head[x];i;i=e[i].nxt)
        if(e[i].to!=anc) dfs(e[i].to,e[i].w-cur,-mark,x);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
    dfs(1,0,1,0);
    
    if(fst) printf("1");
    else printf("%lld",max(0ll,r-l+1));
    return 0;
}
Problem E

 

一般此类对于每个点都有限制条件的题目(如BZOJ 3573),基本上都能保证确定一点就能推出全图

此时只要判断起点能有多少种取值就行了。(推导时还要再耐心些,不要一上来就觉得题目不可做)

 

posted @ 2018-07-08 21:02  NewErA  阅读(170)  评论(0编辑  收藏  举报