炸裂,不想废话。。

T1 邻面合并

诡异的数据范围和时限提示了装压,装压重点在于压什么,状态不好记录,但是数据很小,于是可以只压分割点,转移的时候暴力 check。具体实现合并时细节较多。具体参照代码。

#include<bits/stdc++.h>
using namespace std;
int dp[101][1<<8],n,m,mp[101][9],ans=0x3f3f3f3f;
inline bool check(int id,int sta)
{   int lst=0;
    for(int i=1;i<=m;++i)
    {   if(sta&(1<<i-1))lst=1;
        if((sta&(1<<i-1)) and !mp[id][i])return 0;
        if(!mp[id][i])lst=0;
        if(mp[id][i] and !lst)return 0;
    }
    return 1;
}
inline bool pan(int id,int sta1,int sta2,int wei)
{   for(int i=wei;i<=m;++i)
    {   if((sta1&(1<<i-1)) and !mp[id][i])return 0;
        if((sta2&(1<<i-1)) and !mp[id-1][i])return 0;
        if(mp[id][i]!=mp[id-1][i])return 1;
        if((sta1&(1<<i-1))!=(sta2&(1<<i-1)))return 1;
        if(!mp[id][i] and !mp[id-1][i])break;
    }
    return 0;
}
inline int merge(int id,int sta1,int sta2)
{   int tot=0;
    for(int i=1;i<=m;++i)
    {   if(sta1&(1<<i-1) and sta2&(1<<i-1))tot+=pan(id,sta1,sta2,i);
        else if(sta2&(1<<i-1))++tot;
    }
    return tot;
}
signed main()
{   freopen("merging.in","r",stdin);
    freopen("merging.out","w",stdout);
    scanf("%d%d",&n,&m);memset(dp,0x3f3f3f3f,sizeof(dp));dp[0][0]=0;
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&mp[i][j]);
    for(int i=1;i<=n;++i)
    {   for(int j=0;j<(1<<m);++j)
        {   if(!check(i,j))continue;
            for(int k=0;k<(1<<m);++k)
            if(check(i-1,k))dp[i][j]=min(dp[i][j],dp[i-1][k]+merge(i,k,j));
        }
    }
    for(int i=0;i<(1<<m);++i)if(check(n,i))ans=min(ans,dp[n][i]);
    printf("%d\n",ans);
}
T2 光线追踪

考虑每个矩形可以遮盖的幅度,一个矩形一定遮住一段连续区间,并且是一条横边一条竖边,可以将端点和询问的斜率离散化然后线段树维护。更新就是区间修改,对于同一角度的横边,高度较小的优先,如果相同编号大的优先,因为是直接覆盖,竖边同理。注意特判 0。

#include<bits/stdc++.h>
#define N 500005
#define int long long
#define inf 999999999999999999 
using namespace std;
struct qq{int id,shu;};struct qqq{int opt,a,b,c,d;}Q[N];
struct seg
{   struct Tree{int id,minn;}tree[N*4];
    inline void ins(int x,int l,int r,int L,int R,int shu,int id)
    {   if(l>=L and r<=R){if((!tree[x].id or tree[x].minn>=shu))tree[x].id=id,tree[x].minn=shu;return;}
        int mid=(l+r)>>1;
        if(mid<R)ins(x<<1|1,mid+1,r,L,R,shu,id);
        if(mid>=L)ins(x<<1,l,mid,L,R,shu,id);
    }
    inline qq query(int x,int l,int r,int pos)
    {   if(l==r)return (qq){tree[x].id,tree[x].minn};
        int mid=(l+r)>>1;qq tmp;
        if(mid<pos)tmp=query(x<<1|1,mid+1,r,pos);
        else tmp=query(x<<1,l,mid,pos);
        if(!tmp.id)return (qq){tree[x].id,tree[x].minn};
        if(!tree[x].id)return tmp;
        if(tree[x].minn<tmp.shu)tmp=(qq){tree[x].id,tree[x].minn};
        else if(tree[x].minn==tmp.shu and tree[x].id>tmp.id)tmp=(qq){tree[x].id,tree[x].minn};
        return tmp;
    }
}h,s;
int tot,q;
double san[N*4];
inline bool cmp(qq x1,qq x2,int x,int y)
{   if(!x)return Q[x1.id].b<Q[x2.id].b;
    if(!y)return Q[x1.id].a<Q[x2.id].a; 
    if(y*x2.shu==x1.shu*x)return x2.id<x1.id;return y*x2.shu>x1.shu*x;
}
signed main()
{   freopen("raytracing.in","r",stdin);
    freopen("raytracing.out","w",stdout);
    scanf("%lld",&q);
    for(int i=1;i<=q;++i)
    {   scanf("%lld",&Q[i].opt);
        if(Q[i].opt==1)
        {   scanf("%lld%lld%lld%lld",&Q[i].a,&Q[i].b,&Q[i].c,&Q[i].d);
            san[++tot]=Q[i].c!=0?1.0*Q[i].b/(1.0*Q[i].c):inf;
            san[++tot]=Q[i].a!=0?1.0*Q[i].b/(1.0*Q[i].a):inf;
            san[++tot]=Q[i].a!=0?1.0*Q[i].d/(1.0*Q[i].a):inf;
        }
        else
        {   scanf("%lld%lld",&Q[i].a,&Q[i].b);
            san[++tot]=Q[i].a!=0?1.0*Q[i].b/(1.0*Q[i].a):inf;
        }
    }
    sort(san+1,san+1+tot);tot=unique(san+1,san+1+tot)-san-1;
    for(int i=1;i<=q;++i)
    {
        if(Q[i].opt==1)
        {   int pos1=lower_bound(san+1,san+1+tot,Q[i].c!=0?1.0*Q[i].b/(1.0*Q[i].c):inf)-san;
            int pos2=lower_bound(san+1,san+1+tot,Q[i].a!=0?1.0*Q[i].b/(1.0*Q[i].a):inf)-san;
            int pos3=lower_bound(san+1,san+1+tot,Q[i].a!=0?1.0*Q[i].d/(1.0*Q[i].a):inf)-san;
            h.ins(1,1,tot,pos1,pos2,Q[i].b,i);
            s.ins(1,1,tot,pos2,pos3,Q[i].a,i);
        }
        else
        {   int pos=lower_bound(san+1,san+1+tot,Q[i].a!=0?1.0*Q[i].b/(1.0*Q[i].a):inf)-san;
            qq j1=h.query(1,1,tot,pos);
            qq j2=s.query(1,1,tot,pos);
            if(!j1.id and !j2.id){puts("0");continue;}
            if(!j1.id)printf("%lld\n",j2.id);
            else if(!j2.id)printf("%lld\n",j1.id);
            else if(cmp(j1,j2,Q[i].a,Q[i].b))printf("%lld\n",j1.id);
            else printf("%lld\n",j2.id);
        }
    }
}
T3 百鸽笼

将问题变为求不合法的概率,然后容斥,dp跑的是方案数。

#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
int n,a[31],c[901][901],dp[31][901],ans,sum;
inline void pre()
{   for(int i=0;i<=900;++i)
    {   c[i][0]=1;
        for(int j=1;j<=i;++j)c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    }
}
inline int qpow(int a,int b){int base=1;while(b){if(b&1)base=base*a%mod;a=a*a%mod;b>>=1;}return base;}
inline void solve(int x)
{   memset(dp,0,sizeof(dp));dp[0][0]=1;sum=0;ans=1;
    for(int i=1;i<=n;sum+=a[i]-1,++i)
    {   if(i==x)continue;
        for(int j=i;j>=0;--j)
        for(int t=sum;t>=0;--t)
        if(dp[j][t])for(int k=0;k<a[i];++k)(dp[j+1][t+k]+=dp[j][t]*c[t+k][k]%mod)%=mod;
    }
    for(int i=1;i<n;++i)for(int k=0;k<=sum;++k)ans=(ans+((i&1)?-1:1)*dp[i][k]*c[k+a[x]-1][a[x]-1]%mod*qpow(qpow(i+1,mod-2),a[x]+k)%mod+mod)%mod;
    printf("%lld ",ans);
}
signed main()
{   freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%lld",&n);pre();
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
    for(int i=1;i<=n;++i)solve(i);
}
T4 滑稽树下你和我

这题其实并不那么高深,就是个二分加bfs,因为边上的距离也要考虑,所以状态就定义为一个在点上一个在边上,然后 bfs check即可,比较陌生的是点到线段的最短距离的求法。

#include<bits/stdc++.h>
#define N 1010
#define int long long
using namespace std;
double x[N],y[N],mid;const double eps=1e-6;
int n,a[N],b[N],s,t,tot=1,head[N],vis[N][N],du[N];
struct jj{int to,nxt;}bian[N<<2];queue<pair<int,int>>Q;
inline void add(int u,int v){bian[++tot].to=v;bian[tot].nxt=head[u];head[u]=tot;}
inline double getdis(int i,int j){return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));}
inline double pan(int i,int j,int k){if((x[i]-x[j])*(x[k]-x[j])+(y[i]-y[j])*(y[k]-y[j])>0 and(x[i]-x[k])*(x[j]-x[k])+(y[i]-y[k])*(y[j]-y[k])>0)
return abs((x[j]-x[i])*(y[k]-y[i])-(y[j]-y[i])*(x[k]-x[i]))/getdis(j,k);return min(getdis(i,j),getdis(i,k));}
inline void ed(int i,int j){if(!vis[i][j] and pan(i,a[j],b[j])<=mid)vis[i][j]=1,Q.push(make_pair(i,j));}
inline bool check(double lim)
{   while(!Q.empty())Q.pop();for(int i=head[s];i;i=bian[i].nxt)ed(t,i>>1);for(int i=head[t];i;i=bian[i].nxt)ed(s,i>>1);
    while(!Q.empty())
    {   int x=Q.front().first,y=Q.front().second;Q.pop();
        if(du[x]==1 and ((du[a[y]]==1 and getdis(x,a[y])<=lim)or(du[b[y]]==1 and getdis(x,b[y])<=lim)))return 1;
        for(int i=head[x];i;i=bian[i].nxt)ed(bian[i].to,y),ed(b[y],i>>1),ed(a[y],i>>1);
    }
    return 0;
}
signed main()
{   freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    scanf("%lld%lld%lld",&n,&s,&t);
    for(int i=1;i<=n;++i)scanf("%lf%lf",&x[i],&y[i]);
    for(int i=1;i<n;++i)scanf("%lld%lld",&a[i],&b[i]),add(a[i],b[i]),add(b[i],a[i]),++du[a[i]],++du[b[i]];
    double l=getdis(s,t),r=5e5;while(r-l>eps){mid=(l+r)/(2.0);memset(vis,0,sizeof(vis));if(check(mid))r=mid;else l=mid;}
    printf("%.10lf\n",l);
}