CSP模拟day8

A. 模板题

30pts

枚举ai,bj  预处理{c}  O(nm)

45pts

60pts

不用前缀和用树状数组,多个log

数组没开两倍

100pts

ci=∑ajbi-j

考虑区间转前缀solve(r)-solve(l-1)

由于q很小,可以预处理b的前缀和,每次枚举i,O(n)

0pts

注意模数相加会爆int,记得开long long(真不是故意卡成0分的)不要那么自信,试一下我给的大样例啊!!!

变量名污染CE的

NTT打挂了

复制代码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long u64;
#define int long long 
const int MAX=6e6+10;
const int p=1145141999;
int a[MAX],b[MAX<<1];
int n,m,q,w;
u64 seed;
u64 rnd()
{
    u64 x = seed;
    x ^= x << 13;
    x ^= x >> 7;
    x ^= x << 17;
    return seed = x;
}
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
inline int work(int x){
    if(x<0)  return 0;
    int res=0;
    for(int i=0;i<=min(x,n);++i)  
        res=(res+a[i]*b[x-i]%p)%p;
    return res;
}
signed main(){
    scanf("%d%d%llu%lld",&n,&m,&seed,&w);
    scanf("%d",&q);
    for(int i=0;i<=n;++i)a[i]=rnd()%w;
    for(int i=0;i<=m;++i)b[i]=rnd()%w;
    for(int i=1;i<=m+n;++i)  b[i]=(b[i]+b[i-1])%p;
    int l,r;
    for(int i=1;i<=q;++i){
        l=read();r=read();
        printf("%lld\n",(work(r)-work(l-1)+p)%p);
    }
    
}
View Code
复制代码

B. THUSC

60pts

在前两个子任务放了n=1,某些打法需要特判

70pts

对每一个斜率找直线时,对所有点计算b,再统计相同b的个数

O(num*nlog)

用double记录斜率,损失精度,最后一个子任务有卡精度的点

100pts

题目描述非常没有人性清晰,对所有x,y排序后有多少不同的序列

显然kx,ky的序列是相同的,同理x/y相近的序列会有很多相同

设B=ax+by,b=(B-ax)/y=-(x/y)a+B/y

形如Y=kX+b(x,y已经固定,所以B/y为常数,及时y=ky时绝对大小不同,但相对大小相同)

以ai为横坐标,bi为纵坐标,斜率为-x/y的斜线经过每个点,以纵截距排列

 以样例为例,当某一x,y时

 

排名为1,2,3,4,5

于是,我们计算每两点之间斜率,以Δx/gxd(Δx,Δy),Δy/gxd(Δx,Δy)的形式保证精度,并用(化简后的)Δy*x1+Δx*y1记录为哪一条直线

一点计算:y1=-(x/y)x1+b   y1*y=-x*x1+b*y  y1*y+x1*x=b*y  b*y在确定x,y时为常数

分为两种情况

1、同一直线,线上的点可以任意排名,方案数为线上点数量的阶乘

2、斜率相同,即x,y相同,对不同的直线方案相乘,∏cnt!

而不同时经过两点的斜率,缓慢移动,使它经过两点,此时的若干排名包括之前斜率的排名

如上图排名包括在

因此每个斜率还会算上略大于略小于它的斜率的排名

每两个斜率之间,夹着的斜率排名会被算两次

如上图排名1,4,3,2,5在下图中也会出现

设一共有num条不同的斜率,最后方案为(∑∏cnt!)-(num-1)

复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAX=1010;
const int mod=998244353;
int n,dertx,derty,x,cnt,num,res,ans,f[MAX];
pair<int,int>a[MAX];
struct node{
    int x,y,b;
    friend bool operator==(const node &a,const node &b){
        return a.x==b.x&&a.y==b.y&&a.b==b.b;
    }
} c[MAX*MAX];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
int gcd(int a,int b){
    if(!b)  return a;
    return gcd(b,a%b);  
}inline bool cmp(node a,node b){
    if(a.x==b.x&&a.y==b.y)  return a.b<b.b;
    if(a.x==b.x)  return a.y<b.y;
    return a.x<b.x;
}
signed main(){
//    freopen("1.txt","r",stdin);
    n=read();f[0]=1;
    for(int i=1;i<=n;++i)  f[i]=f[i-1]*i%mod;
    for(int i=1;i<=n;++i)  a[i]={read(),read()};
    sort(a+1,a+n+1);
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j){
            dertx=a[j].first-a[i].first;derty=a[i].second-a[j].second;
            if(!dertx||derty<=0)  continue;
            x=gcd(dertx,derty);dertx/=x;derty/=x;
            c[++cnt]={dertx,derty,derty*a[i].first+dertx*a[i].second};
        }
    sort(c+1,c+cnt+1,cmp);int j;
    for(int i=1;i<=cnt;i=j){
        j=i;++num;res=1;
        while(j<=cnt&&c[j].x==c[i].x&&c[j].y==c[i].y){
            int k=j;
            while(k<=cnt&&c[k]==c[j])  ++k;
            res=res*f[(int)sqrt(k-j<<1)+1]%mod;
            j=k;
        }ans=(ans+res)%mod;
    }ans=(ans-num+1)%mod;
    printf("%d",ans);
}
View Code
复制代码

C. 8ady

Sub1

next_permutation本来就是从小到大枚举的,不用排序

扫到第k个符合{b}的{a}就结束,否则会T成0

不开O2也会T

Sub4

bi为a1,……amin(i+m-1,n)中最小且不在b1,……bi-1的数

{b}最后m-1个数,为n-m+2,……n-1,n,

反证,若这m-1个数出现在之前,则要求它是在{a}某一段长为m的序列中第m大的数,显然没有m-1个数比它们大。且变化后同样

若max(b1,……bi-1)>bi,则bi为ai+m-1

反证,若bi出现在ai+m-1前,则它会出现在bi前

把这些bi提出,最后剩下{b}单调递增,依次填入未填的空,就是最小排列

Sub2

每个数出现可能有min(i+m-1,n),在填入前没有相对前后,不知道谁会占用谁的位置

转化为n变小,m,k不变的子问题

枚举i位填入j的方案数,递增j

注意固定i,j后,计算方案数枚举未填入的非j的数的方案时,填入一位后面的数少一个可能位置

O(n^3)

100pts

排列的方案数为阶乘形式,20!>1e18,所以可以直接固定前n-20项为ai=i的前缀

后20个数有n-20个可能位置已经被占用,方案数要减去

O(log^3+n)

复制代码
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e6+10;
#define int long long
int n,m,K,ab,a[MAX],b[MAX],ans[MAX],d[22],cnt;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
signed main(){
//    freopen("in.txt","r",stdin);
    n=read();m=read();K=read();
    for(int i=1;i<=n;++i){
        ab=read();
        if(b[cnt]>ab)  ans[i+m-1]=ab;
        else  b[++cnt]=ab; 
    }int p=max(0ll,cnt-20);
    for(int i=1;i<=p;++i)  a[i]=i;
    for(int i=1;i<=cnt-p;++i)
        d[i]=min(m+i+p-1,cnt)-p;
    for(int i=1;i<=cnt-p;++i){
        for(int j=1;j<=cnt-p;++j)  --d[j];
        for(int j=1;j<=cnt-p;++j){
            if(d[j]<0)  continue;
            int res=1,c=0;
            for(int k=1;k<=cnt-p;++k){
                if(d[k]<0||k==j)  continue;
                res*=d[k]-c;++c;
                if(res>=K)  break;
            }if(res>=K){
                a[i+p]=j+p;d[j]=-1;break;
            }K-=res;
        }
    }cnt=0;
    for(int i=1;i<=n;++i){
        if(ans[i])  printf("%d ",ans[i]);
        else  printf("%d ",b[a[++cnt]]);
    }
}
View Code
复制代码

D. 白子说话

1/2的意思每个黑子在初始时1/2概率存在

对于所有可能的白点1/4概率度数为奇

插头dp求欧拉图可能个数

下以x+y偶为例

x奇时,连接(i-1,j)(i+1,j)

x偶时,连接(i,j-1)(i,j+1)

每次插入(2x,2y),(2x-1,2y+1),对一列3个白点分别讨论是否存在,度数是否为奇,相互是否连通,所在连通块是否有奇数点

如插入(2,2),(1,3)  状态存的点为(0,5)(2,1)(2,3)

考虑如何通过枚举(2x,2y)(2x-1,2y+1)是否存在,确定转移到的状态,积累方案数

细节解释在代码

复制代码
#include<bits/stdc++.h>
using namespace std;
const int MAX=610;
const int mod=998244353;
#define int long long
#define dp123 dp[x][y][s1][s2][s3][s4]
int n,c[110][9],b[110][9],f[MAX],inv4=748683265,fa[9],id[9],x,y;
int dp[55][7][10][10][10][10];
map<pair<int,int>,bool>mp;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
int father(int x){
    if(fa[x]==x)  return x;
    return fa[x]=father(fa[x]);
}inline void merge(int x,int y){
    x=father(x);y=father(y);
    if(x!=y)  fa[x]=y;
}
inline int solve(int a[][9]){
    memset(dp,0,sizeof(dp));mp.clear();
    for(int i=0;i<100;++i)
        for(int j=0;j<6;++j)
            if(a[i][j]){
                if(i&1)  mp[{i-1,j}]=mp[{i+1,j}]=1;
                else  mp[{i,j-1}]=mp[{i,j+1}]=1;
            }
    int ans=mp.size()*f[n]%mod*inv4%mod,sum=n;
    dp[0][0][0][0][0][0]=1;
    for(int x=0;x<=51;++x)
        for(int y=0;y<3;++y){
            sum-=(x&&a[(x<<1)-1][y<<1|1])+a[x<<1][y<<1];
            for(int s1=0;s1<8;++s1)
                for(int s2=0;s2<8;++s2)
                    for(int s3=0;s3<8;++s3)
                        for(int s4=0;s4<8;++s4)
                            if(dp123){
                                for(int p=0;p<=(x&&a[(x<<1)-1][y<<1|1]);++p)
                                    for(int q=0;q<=a[x<<1][y<<1];++q){
                                        int ss1=s1,ss2=s2,ss3=s3,ss4=s4;
                                        if(ss1>>y&1)  ss1^=1<<y;//删去存储的原y的状态 
                                        ss1|=((p|q)<<y)|((q&&y)<<y-1);//p或q存在时,y存在;q存在时,y-1存在 
                                        if(ss2>>y&1)  ss2^=1<<y;
                                        ss2^=((p^q)<<y)|((q&&y)<<y-1);//存在一个p或q,y多一条边 
                                        //0 1 2为原状态存的点,3为现在的y点
                                        //根据原状态和pq连边,并查集判连通 
                                        for(int i=0;i<=3;++i)  fa[i]=i,id[i]=i;
                                        id[y]=3;
                                        if(s3&1)  merge(0,1);
                                        if((s3>>1)&1)  merge(0,2);
                                        if((s3>>2)&1)  merge(1,2);
                                        if(p)  merge(y,3);
                                        if(q&&y)  merge(y-1,3);
                                        ss3=(father(id[0])==father(id[1]))|((father(id[0])==father(id[2]))<<1)
                                        |((father(id[1])==father(id[2]))<<2);
                                        if(ss4>>y&1)  ss4^=1<<y;
                                        //(!y&&q) (2x,-1)只有这一条边,度数为奇且与y连通
                                        //当p存在,即原y与现y连通, 
                                        //(s4>>y&1)原y所在连通块有奇数点;或!(s2>>y&1)原y之前为偶数点,现度数为奇时,现y所在连通块有奇数点 
                                        //注意现y的奇偶性还未确定,不能ss4|=(ss2>>y&1)<<y; 
                                        if((!y&&q)||(p&&((s4>>y&1)||(!(s2>>y&1)))))  ss4|=1<<y;
                                        //某一点连通块有奇数点时,与它相连的点的连通块都有奇数点 
                                        for(int i=0;i<3;++i)
                                            for(int j=i+1;j<3;++j)
                                                if(father(id[i])==father(id[j])){
                                                    ss4|=(ss4>>i&1)<<j;
                                                    ss4|=(ss4>>j&1)<<i;
                                                }
                                        //原y为奇数点时,与它相连的点的连通块都有奇数点
                                        //注意p&&(!(s2>>y&1))时,原y也为奇数点,但在上面在现y里算过 
                                        if(!p&&(s2>>y&1))
                                            for(int i=0;i<=2;++i)
                                                ss4|=(father(id[i])==father(y))<<i;
                                        //向下一状态转移 
                                        if(y==2)  dp[x+1][0][ss1][ss2][ss3][ss4]=(dp[x+1][0][ss1][ss2][ss3][ss4]+dp123)%mod;
                                        else  dp[x][y+1][ss1][ss2][ss3][ss4]=(dp[x][y+1][ss1][ss2][ss3][ss4]+dp123)%mod;
                                        int fg=0;
                                        for(int i=0;i<=3;++i)
                                            if(i^y)  fg|=(father(i)==father(y));
                                        //当原y存在且度数为奇且与现状态3点不连通且所在连通块没有奇数点
                                        //即原y所在连通图为欧拉图,它为此图的最后一个点,此时算入答案,不会算重 
                                        if((s1>>y&1)&&(!(s2>>y&1))&&(!fg)&&(!(s4>>y&1)))  ans=(ans+dp123*f[sum]%mod)%mod;
                                    }
                            }
        }
    return ans;
}
signed main(){
    n=read();f[0]=1;
    for(int i=1;i<=n;++i)  f[i]=(f[i-1]+f[i-1])%mod;
    for(int i=1;i<=n;++i){
        x=read();y=read();
        if((x+y)&1)  b[x][5-y]=1;
        else  c[x][y]=1;
    }printf("%d",(solve(c)+solve(b))%mod);
}
复制代码

先看官方题解

posted @   yisiwunian  阅读(280)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示