冲刺清北营 6

保龄场。蚌了。场上三道题不是不可做就是不可做,然后跑去做组合。然而昨天看了一晚上多项式 \(\gcd\) 搞的我现在还在偏头痛。当然也有可能是咖啡因磕多了。

题目背景怎么全是龙族。

从今天开始戒多项式。

万家灯火

赛时基本想到正解了,但是我本来打算写的是动态开点线段树……觉得这玩意写起来最起码得 5k 就没写。赛后看了看过的代码,全是 5-6k 的,而且是树状数组。可想而知我大概是过不去的。

首先看着就很点分树。然后连通块套路变成点减边。点比较好做。考虑边,边可以把每个点的权值看成有多少点亮的儿子,然后修改就是改点权和在某个数据结构上加减贡献。开个树状数组维护以上信息即可。

题解是算的每个没点亮的节点有几个点亮的儿子就是答案,是一样的。

不打算写。

逃亡

首先转成走到每个点的概率。好了我场上没想到这个直接爆炸。

然后就比较简单了:可以知道 \(n\) 步走到距离为 \(i\) 的某个点的概率是 \([i\equiv n\pmod 2]\dfrac 1{2^n}\dbinom n{\frac{(n+i)}2}\)。推导是格路计数,从 \((0,0)\) 出发,每步可以看做 \((+1,\pm 1)\),最终就是落在线 \(y=i\) 的概率。然后考察经过这个点的概率,发现如果在第一次经过这条线的地方翻折,那么在这条线下边的概率和在上边的概率是一样的,因此经过的概率就是 \(p_i+\sum_{j>i}2p_j\)。那总共就 \(O(nm)\) 个点,对于每个点 \(O(m)\) 算一遍贡献就行了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <bitset>
#include <cstring>
using namespace std;
const int mod=998244353;
int n,m,a[25],jc[12000010],inv[12000010],p[12000010];
bitset<100000000>vis;
int C(int n,int m){
    return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int qpow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*ans*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);jc[0]=inv[0]=inv[1]=1;
    for(int i=1;i<=m;i++)scanf("%d",&a[i]),a[i]+=n;
    sort(a+1,a+m+1);
    for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    int pw=qpow(inv[2],n);
    for(int i=1;i<=n;i++)jc[i]=1ll*jc[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
    for(int i=0;i<=n;i++)if((n&1)==(i&1))p[i]=1ll*pw*C(n,(n+i)>>1)%mod;
    int sum=0;
    for(int i=n;i>=0;i--){
        int ret=p[i];
        p[i]=(p[i]+2ll*sum)%mod;
        sum=(sum+ret)%mod;
    }
    sum=0;
    for(int i=1;i<=m;i++){
        for(int j=-n;j<=n;j++){
            int x=a[i]+j;
            if(vis[x])continue;
            vis[x]=true;
            int ans=0;
            for(int k=1;k<=m;k++){
                if(abs(a[k]-x)<=n)ans=(ans+p[abs(a[k]-x)]-1ll*ans*p[abs(a[k]-x)]%mod+mod)%mod;
            }
            sum=(sum+ans)%mod;
        }
    }
    printf("%d\n",sum);
    return 0;
}

地铁

居然是平面图?压根没看出来。菜的真实。看来我对平面图的理解就仅限于网格图。

平面图最短路转对偶图最小割,然后经过一个点可以看成必须割以这个点为右端点的最长边。然后不会了,因为题解说的似乎有毛病。

拜谢牛子精灵,由于对偶图是一棵树的样子,所以这个限制可以变成割一条边必须割另一条边到根的所有边,这样可以前缀优化建图跑一下。

这题似乎数据很水,有个暴力 95,而且暴力连边也能过。

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <queue>
#include <map>
#include <set>
using namespace std;
const int inf=0x3f3f3f3f;
struct node{
    int v,w,next;
}edge[2000010];
int n,m,k,S,T,cnt,t=1,ans,head[2000010];
void Add(int u,int v,int w){
    edge[++t].v=v;edge[t].w=w;edge[t].next=head[u];head[u]=t;
}
void add(int u,int v,int w){
    Add(u,v,w);Add(v,u,0);
}
queue<int>q;
int head2[2000010],dis[2000010];
bool bfs(int st){
    for(int i=0;i<=cnt;i++)head2[i]=head[i],dis[i]=0;
    dis[st]=1;q.push(st);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=edge[i].next){
            if(edge[i].w&&!dis[edge[i].v]){
                dis[edge[i].v]=dis[x]+1;
                if(edge[i].v==T){
                    while(!q.empty())q.pop();
                    return true;
                }
                q.push(edge[i].v);
            }
        }
    }
    return false;
}
int dfs(int x,int flow){
    if(x==T)return flow;
    int sum=0;
    for(int &i=head2[x];i;i=edge[i].next){
        if(edge[i].w&&dis[edge[i].v]==dis[x]+1){
            int ret=dfs(edge[i].v,min(flow,edge[i].w));
            if(ret){
                edge[i].w-=ret;edge[i^1].w+=ret;
                flow-=ret;sum+=ret;
                if(!flow)break;
            }
            else dis[edge[i].v]=-1;
        }
    }
    return sum;
}
struct line{
    int l,r,v,w;
    bool operator<(const line&s)const{
        return l<s.l;
    }
}eg[1010];
set<line>s;
map<pair<int,int>,int>mp;
bool cmp(line a,line b){
    return a.r-a.l<b.r-b.l;
}
int pos[1010];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        int u,v,w;scanf("%d%d%d",&u,&v,&w);
        pair<int,int>p=make_pair(u,v);
        if(mp.find(p)==mp.end())mp[p]=w;
        else mp[p]=min(mp[p],w);
    }
    int num=0;
    for(pair<pair<int,int>,int> p:mp)eg[++num]={p.first.first,p.first.second,0,p.second};
    m=num;
    sort(eg+1,eg+m+1,cmp);
    S=1;T=2;cnt=2;
    for(int i=2;i<=n;i++){
        cnt++;add(cnt,T,inf);
        s.insert({i-1,i,cnt,inf});
        cnt++;add(cnt,cnt-1,inf);
    }
    for(int i=1;i<=m;i++){
        cnt++;
        for(int j=eg[i].l+1;j<eg[i].r;j++)if(!pos[j])pos[j]=cnt;
        eg[i].v=cnt;
        vector<line>ret;
        for(line x:s){
            if(x.l>=eg[i].l&&x.r<=eg[i].r){
                add(cnt,x.v,x.w);
                ret.push_back(x);
                add(x.v+1,cnt+1,inf);
            }
        }
        for(line x:ret)s.erase(x);
        s.insert(eg[i]);
        add(cnt+1,cnt,inf);cnt++;
    }
    for(line x:s)add(S,x.v,x.w);
    for(int i=1;i<=n;i++)if(!pos[i])pos[i]=S;
    for(int i=1;i<=k;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(pos[u],pos[v]+(pos[v]!=S),inf);add(pos[v],pos[u]+(pos[u]!=S),inf);
    }
    while(bfs(S))ans+=dfs(S,inf);
    if(ans>1e9)ans=-1;
    printf("%d\n",ans);
    return 0;
}
posted @ 2023-04-25 19:40  gtm1514  阅读(16)  评论(0编辑  收藏  举报