6.22联考题解

A:
和某题很像,这题是带修改版本的
考虑把每条边 ( u , v , w ) 边权加到他连接的两点 u , v
当A,B中某人同时取了 u , v ,他获得 2 w 的价值,对差值贡献 ± 2 w
当A,B一人取了 u ,一人取了 v ,各获得 w ,对差值贡献 0

发现将差值 / 2 后和原来取边的情况等价
问题变成了 n 个点,取每个点有点权,A,B轮流取,A想最大化A-B,B反之
显然A,B会轮流取最大权的点

我们只要维护点权排序后奇数位置的权值和偶数位置的权值
带修改,修改=删除+插入
写一棵splay维护

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

ll ans;
inline void read(ll &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
    x^=ans;
}
inline void up(int &a,const int &b){if(a<b)a=b;}
const int maxn = 210000;

int n,m,O;
int s[maxn];
int op[maxn][3],cnt;
struct Splay
{
    int root;
    int son[maxn][2],fa[maxn];
    int siz[maxn];
    ll sum[maxn][2];
    int build(int l,int r)
    {
        int x=(l+r)>>1;
        if(l==r) { siz[x]=1,sum[x][1]=s[x]; return x; }
        if(l!=x) fa[son[x][0]=build(l,x-1)]=x;
        if(x!=r) fa[son[x][1]=build(x+1,r)]=x;
        pushup(x);
        return x;
    }
    void pushup(int x)
    {
        int ls=siz[son[x][0]]&1;
        sum[x][0]=sum[son[x][0]][0]+(ll)ls*s[x]+sum[son[x][1]][ls^1];
        sum[x][1]=sum[son[x][0]][1]+(ll)(ls^1)*s[x]+sum[son[x][1]][ls];
        siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
    }
    void rot(int x,int &k)
    {
        int y=fa[x],z=fa[y];
        if(y!=k) son[z][son[z][1]==y]=x;
        else k=x;
        fa[x]=z;
        int l=son[y][1]==x;
        fa[son[y][l]=son[x][!l]]=y;
        fa[son[x][!l]=y]=x;
        pushup(y);
    }
    void splay(int x,int &k)
    {
        for(;x!=k;rot(x,k))
        {
            int y=fa[x],z=fa[y];
            if(y!=k) rot(((son[y][1]==x)^(son[z][1]==y))?x:y,k);
        }
        pushup(x);
    }
    int go(int x,int t)
    {
        while(son[x][t]) x=son[x][t];
        return x;
    }
    void Del(int x)
    {
        splay(x,root);
        int y=go(son[x][0],1);
        if(y) 
        {
            splay(y,son[x][0]);
            root=fa[son[y][1]=son[x][1]]=y;
        }
        else fa[root=son[x][1]]=0;
        pushup(root);
    }
    void Ins(int x)
    {
        son[x][0]=son[x][1]=0;
        sum[x][0]=0,sum[x][1]=s[x];
        siz[x]=1;

        for(int p=root,l;;p=son[p][l])
        {
            l=s[p]<s[x];
            if(!son[p][l])
            {
                fa[son[p][l]=x]=p;
                splay(x,root);
                break;
            }
        }
    }
    ll cal() { return (siz[root]&1)?sum[root][1]-sum[root][0]:sum[root][0]-sum[root][1]; }
}tr;

int main()
{
    freopen("round.in","r",stdin);
    freopen("round.out","w",stdout);

    scanf("%d%d%d",&n,&m,&O);
    tr.root=tr.build(1,n);
    for(int i=1;i<=m;i++)
    {
        int type; scanf("%d",&type);
        if(type==1)
        {
            ll u,v; int w; read(u),read(v); scanf("%d",&w);
            ans=0;
            ++cnt,op[cnt][0]=u,op[cnt][1]=v,op[cnt][2]=w;
            tr.Del(u); if(u!=v) tr.Del(v);
            s[u]+=w,s[v]+=w;
            tr.Ins(u); if(u!=v) tr.Ins(v);
        }
        else
        {
            ll k; read(k);
            ans=0;
            int u=op[k][0],v=op[k][1],w=op[k][2];
            tr.Del(u); if(u!=v) tr.Del(v);
            s[u]-=w,s[v]-=w;
            tr.Ins(u); if(u!=v) tr.Ins(v);
        }
        ans=tr.cal()>>1;
        printf("%lld\n",ans);
        ans*=O;
    }

    return 0;
}

B:
突然就成了板子题?
设起点从左到右为 a 1 , a 2.... a n ,终点从左到右 b 1 , b 2..... b n 因为不能路径相交,最终路径显然是 a 1 > b 1 , a 2 > b 2 , a 3 > b 3..... a n > b n

先考虑怎么求点 S T ,不经过障碍点的方案数
T 看作障碍点,设 f [ i ] 表示 S 到第 i 个障碍点,中途不经过其他障碍点的方案数,转移的时候容斥一下,总方案数减不合法的,不合法的通过枚举遇到的第一个障碍点计算 f [ i ] = w a y s ( S , i ) f [ j ] w a y s ( j , i )

然后设 M [ i ] [ j ] 表示 a i b j 不经过障碍点的方案数,根据某定理,这个 n n 的矩阵的行列式就是路径两两不想交的总方案数
感受一下似乎就是个容斥

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxc = 210000;
const int maxn = 405;
const int mod  = 998244353;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}
inline void dec(int &a,const int &b){a-=b;if(a<0)a+=mod;}

int pw(int x,int k)
{
    int re=1;
    for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
        re=(ll)re*x%mod;
    return re;
}
int inv(int x){ return pw(x,mod-2); }
int s[maxc],invs[maxc];
void pre()
{
    s[0]=1; for(int i=1;i<maxc;i++) s[i]=(ll)s[i-1]*i%mod;
    invs[maxc-1]=inv(s[maxc-1]);
    for(int i=maxc-2;i>=0;i--) invs[i]=(ll)invs[i+1]*(i+1)%mod;
}
int C(int i,int j){ return i>=j?(ll)s[i]*invs[j]%mod*invs[i-j]%mod:0; }

int n,m,p,q;
struct point
{
    int x,y;
    int cal(point z)
    {
        if(z.x<x||z.y<y) return 0;
        return C(z.x-x+z.y-y,z.x-x);
    }
}st[maxn],ed[maxn],pi[maxn];
inline bool cmp(const point x,const point y){ return x.x==y.x?x.y<y.y:x.x<y.x; }

namespace Gauss
{
    int n;
    int a[maxn][maxn];
    int Solve()
    {
        for(int i=1;i<n;i++)
        {
            if(!a[i][i])
            {
                int k=0;
                for(int j=i+1;j<=n;j++) if(a[j][i]) { k=j;break; }
                if(k)
                {
                    for(int j=i;j<=n;j++) swap(a[i][j],a[k][j]);
                }
                else continue;
            }
            int ii=inv(a[i][i]);
            for(int j=1;j<=n;j++) if(a[j][i]&&j!=i)
            {
                int k=(ll)ii*a[j][i]%mod;
                for(int l=i;l<=n;l++) dec(a[j][l],(ll)a[i][l]*k%mod);
            }
        }
        int ans=1;
        for(int i=1;i<=n;i++) ans=(ll)ans*a[i][i]%mod;
        return ans;
    }
}

int f[maxn];

int main()
{
    freopen("canal.in","r",stdin);
    freopen("canal.out","w",stdout);

    pre();

    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1;i<=p;i++) st[i].x=0,scanf("%d",&st[i].y);
    for(int i=1;i<=p;i++) ed[i].x=n,scanf("%d",&ed[i].y);
    for(int i=1;i<=q;i++) scanf("%d%d",&pi[i].x,&pi[i].y);

    sort(st+1,st+p+1,cmp);
    sort(ed+1,ed+p+1,cmp);
    sort(pi+1,pi+q+1,cmp);

    Gauss::n=p;
    for(int i=1;i<=p;i++)
    {
        for(int j=1;j<=q;j++)
        {
            f[j]=st[i].cal(pi[j]);
            for(int k=1;k<j;k++) dec(f[j],(ll)f[k]*pi[k].cal(pi[j])%mod);
        }
        for(int j=1;j<=p;j++)
        {
            int &temp=Gauss::a[i][j];
            temp=st[i].cal(ed[j]);
            for(int k=1;k<=q;k++) dec(temp,(ll)f[k]*pi[k].cal(ed[j])%mod);
        }
    }
    printf("%d\n",Gauss::Solve());

    return 0;
}

C:
Simpson积分能拿50pt
求凸多边形和所有圆并面积,圆并面积,凸多边形面积
算面积格林公式变二重积分,然后找每条线段,每个圆的圆弧,在边界上的部分,叉积积分一下算面积

反正我不会

猜你喜欢

转载自blog.csdn.net/l_0_forever_lf/article/details/80779012
今日推荐