csp-s模拟测试52-53
阅读原文时间:2023年07月09日阅读:2

留坑….

改完题再说吧。

**留坑….


最近考得什么鬼??
模拟53
T1 u(差分)
**


**一道差分题????
然而考场没有想到如何维护斜率上的差分,事后经miemeng和cyf的生(xuan)动(xue)讲解大概是懂了
联想如何维护一个矩形的差分??
假设我们区间修改为L1,R1,L2,R2,
我们在L2+1,R2+1处+1,在L1,R1处+1,在L2+1,R1处-1,在L1,R2+1处-1,然后画图发现从左向右递推时
我们维护的二维前缀和中有+1的部分恰好是区间修改的部分
同理,我们现在维护出一个三角形
首先考虑斜率,假设L1,R1左上角,len为直角边长度,在L1,R1加一,在L1+len,R1+len处-1我们按斜率递推一下就得到斜线上的贡献,然后在考虑直线上的贡献,我们在维护列上的前缀和,然后再按行递推一下就可以画出三角形
**

1 #include
2 #define MAXN 2101
3 #define int long long
4 using namespace std;
5 int hang[MAXN][MAXN];int lie[MAXN][MAXN];int sum[MAXN][MAXN];
6 int n,q;
7 int read(){
8 int x=0;char c=getchar();
9 while(c<'0'||c>'9')c=getchar();
10 while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} 11 return x; 12 } 13 int ans=0; 14 signed main(){ 15 n=read();q=read(); 16 for(int i=1;i<=q;++i){ 17 int l=read();int r=read();int len=read();int s=read(); 18 lie[l][r-1]-=s; 19 lie[l+len][r-1]+=s; 20 sum[l][r]+=s; 21 sum[l+len][r+len]-=s; 22 } 23 for(int i=1;i<=2*n;++i){ 24 for(int j=1;j<=2*n;++j){ 25 sum[i][j]=sum[i][j]+sum[i-1][j-1]; 26 } 27 } 28 for(int j=1;j<=2*n;++j){ 29 for(int i=1;i<=2*n;++i){ 30 lie[i][j]=lie[i][j]+lie[i-1][j]; 31 } 32 } 33 for(int j=2*n;j>=1;--j){
34 for(int i=1;i<=n*2;++i){
35 hang[i][j]=sum[i][j]+lie[i][j];
36 hang[i][j]+=hang[i][j+1];
37 }
38 }
39 for(int i=1;i<=n;++i){
40 for(int j=1;j<=n;++j)
41 ans^=hang[i][j];
42 }
43 printf("%lld\n",ans);
44 }

**
T2 v(哈希表,记忆化搜索)
**


第一学了哈系表,因为当前决策不明,而且n的范围很小,所以就记忆化搜索了????

1 #include
2 #define MAXN 31000001
3 #define int long long
4 using namespace std;
5 int read(){
6 int x=0;char c=getchar();
7 while(c<'0'||c>'9')c=getchar();
8 while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} 9 return x; 10 } 11 int k;int n; 12 const int mod=30000001; 13 struct map_hash{ 14 struct node{int to,n;double val;int len;}e[31000001]; 15 int tot=0; 16 int head[MAXN];int len=0; 17 double &operator[](int state){ 18 int st=state*len%mod+1; 19 for(int i=head[st];i;i=e[i].n){ 20 if(e[i].to==state&&e[i].len==len) 21 return e[i].val; 22 } 23 e[++tot].to=state; 24 e[tot].val=-1.0; 25 e[tot].len=len; 26 e[tot].n=head[st]; 27 head[st]=tot; 28 return e[tot].val; 29 } 30 }f; 31 int base[MAXN]; 32 int getnum(int state,int k){ 33 if(k==0)return (state>>1);
34 int kx=(state>>(k+1));
35 return (kx<<(k))|(state&base[k-1]); 36 } 37 double DFS(int state,int len){ 38 if(len==n-k)return 0; 39 f.len=len; 40 if(f[state]!=-1.0)return f[state]; 41 double sum=0; 42 for(int i=1;i<=len/2;++i){ 43 int ok1=(state>>(i-1))&1;
44 int find1=getnum(state,i-1);
45 int ok2=(state>>(len-i))&1;
46 int find2=getnum(state,len-i);
47 sum+=2*max(DFS(find1,len-1)+ok1,DFS(find2,len-1)+ok2);
48 }
49 if(len&1){
50 int kx=len/2+1;
51 int ok1=(state>>(kx-1))&1;
52 int find1=getnum(state,kx-1);
53 sum+=DFS(find1,len-1)+ok1;
54 }
55 sum=sum/len;
56 f.len=len;
57 return f[state]=sum;
58 }
59 int st=0;
60 signed main(){
61 n=read();k=read();
62 base[0]=1;
63 for(int i=1;i<=n;++i)base[i]=base[i-1]|(1<<i);
64 for(int i=1;i<=n;++i){
65 char x=getchar();
66 if(x=='W')st|=1<<(i-1);
67 }
68 double ans=DFS(st,n);
69 printf("%.7lf\n",ans);
70 }

T3 w(神仙DP)


第一次作二元组DP很神仙。

性质:1.遇到需要反的边一定要反,遇到不能反的边一定不能反

2.任何操作数其实等于树中翻过的奇数点数/2

所以DP[1/0].fir表示是当前节点是/否向上反边时的计数点个数,sec表示最小路径长度

所以转移时w1,w0分别表示子树贡献,定义同上

转载校外大佬博客(实际是因为自己懒得写了QAQ)

1 #include
2 using namespace std;
3 #define min(a,b) (a)<(b)?(a):(b) 4 #define inf 0x7ffffff 5 #define MAXN 1000000 6 #define int long long 7 int read(){ 8 int x=0;char c=getchar(); 9 while(c<'0'||c>'9')c=getchar();
10 while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
11 return x;
12 }
13 struct node{
14 int fir;int sec;
15 friend node operator +(node a,node b){
16 return (node){a.fir+b.fir,a.sec+b.sec};
17 }
18 };
19 inline node minn(node a,node b){
20 if(a.fir==b.fir)return a.sec<b.sec?a:b;
21 return a.fir<b.fir?a:b;
22 }
23 struct no{
24 int n,to,w;
25 }e[MAXN];
26 int n;
27 int head[MAXN],tot;
28 void add(int u,int v,int w){
29 e[++tot].n=head[u];e[tot].to=v;e[tot].w=w;head[u]=tot;
30 }
31 node dp[MAXN][2];int fa[MAXN];
32 void DP(int x,int faa){
33 node w0=(node){0,0},w1=(node){inf,inf};
34 node t0=(node){0,0},t1=(node){inf,inf};
35 for(int i=head[x];i;i=e[i].n){
36 int to=e[i].to;
37 if(to==faa)continue;
38 DP(to,x);
39 t0=w0,t1=w1;
40 w0=minn(t0+dp[to][0],t1+dp[to][1]);
41 w1=minn(t0+dp[to][1],t1+dp[to][0]);
42 }
43 dp[x][0]=minn((node){w0.fir,w0.sec},(node){w1.fir+1,w1.sec});
44 dp[x][1]=minn((node){w0.fir+1,w0.sec+1},(node){w1.fir,w1.sec+1});
45 if(fa[x]==0){
46 dp[x][1]=(node){inf,inf};
47 }
48 else if(fa[x]==1){
49 dp[x][0]=(node){inf,inf};
50 }
51 }
52 void DFS(int x,int faa){
53 for(int i=head[x];i;i=e[i].n){
54 int to=e[i].to;
55 if(to==faa)continue;
56 int w=e[i].w;
57 fa[to]=w;
58 DFS(to,x);
59 }
60 }
61 signed main(){
62 n=read();
63 for(int i=1;i<=n-1;++i){
64 int u,v,a,b;
65 u=read();v=read();a=read();b=read();
66 if(b!=2){add(u,v,a^b);add(v,u,a^b);}
67 else add(u,v,b),add(v,u,b);
68 }
69 DFS(1,0);
70 DP(1,0);
71 printf("%lld %lld\n",dp[1][0].fir/2,dp[1][0].sec);
72 }


模拟52

T1平均数(归并排序+二分)


发现自己一个小时没有思路,放弃……..

一直以为是神仙数据结构…..

其实只要二分平均值后将所有值减去该值,查找和<0的区间个数,

怎么求,归并排序就好了…..

1 #include
2 #define MAXN 101000
3 #define int long long
4 #define inf 0.00001
5 using namespace std;
6 double sum[MAXN],kx[MAXN];double zzn[MAXN];double maxn;
7 int read(){
8 int x=0;char c=getchar();
9 while(c<'0'||c>'9')c=getchar();
10 while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} 11 return x; 12 } 13 int anss=0;int n;int a[MAXN];int k; 14 void guibing(int l,int r){ 15 if(l==r){kx[l]=sum[l];return ;} 16 int mid=(l+r)>>1;
17 guibing(l,mid);guibing(mid+1,r);
18 int le=l;int re=mid+1;int mm=0;
19 while(le<=mid&&re<=r){
20 if(kx[re]<kx[le]){zzn[++mm]=kx[le];le++;}
21 else{
22 zzn[++mm]=kx[re];
23 anss+=(le-l);
24 re++;
25 }
26 }
27 while(le<=mid){
28 zzn[++mm]=kx[le];le++;
29 }
30 while(re<=r){
31 zzn[++mm]=kx[re];anss+=(le-l);
32 re++;
33 }
34 for(int i=l;i<=r;++i){
35 kx[i]=zzn[i-l+1];
36 }
37 }
38 int work(double len){
39 anss=0;
40 for(int i=1;i<=n;++i){
41 sum[i]=sum[i-1]+((double)a[i]-len);
42 if(sum[i]<0)anss++;
43 kx[i]=0.0;
44 }
45 guibing(1,n);
46 return anss;
47 }
48 void second_divied(){
49 double l=0;double r=maxn;
50 while(l+inf<r){
51 double mid=(l+r)/2.000;
52 if(work(mid)<k)l=mid;
53 else r=mid;
54 }
55 if(work(l)==k)printf("%.4lf\n",l);
56 else printf("%.4lf\n",r);
57 }
58 signed main(){
59 n=read();k=read();
60 for(int i=1;i<=n;++i)a[i]=read(),maxn=max(maxn,(double)a[i]);
61 second_divied();
62 }

***********************

思路积累:

1.求排名第k小转化为有k个比他小的序列

2.平均数可以二分后,让所有数减去该值。

T2涂色游戏(组合数学)


考场死刚…..

最后式子推的基本对了,但是还是有点问题,快速幂打错怒丢30!!!

首先要处理好j,k两列颜色种类有x个颜色交集时的方案数

还要与处理出在n行放k种颜色的方案数

然后转移时发现对于该列有j种颜色,我们先乘f[n][j]/C(p,j)表明我们并不知道所放颜色的具体颜色,只知道一个排列顺序,

对于每一个上一列的状态我们可以转移到该列,

然后乘上相应组合数求出上一列颜色固定时下一列的颜色种类的方案,再乘上排列方式即可

1 #include
2 #define int long long
3 #define MAXN 1100
4 using namespace std;
5 int read(){
6 int x=0;char c=getchar();
7 while(c<'0'||c>'9')c=getchar();
8 while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} 9 return x; 10 } 11 int n,m,p,q; 12 int dp[MAXN][MAXN]; 13 int jie[MAXN],ni[MAXN],ni_c[MAXN]; 14 const int mod=998244353ll; 15 int C(int x,int y){ 16 if(y>x)return 0;
17 if(y==0)return 1;
18 return jie[x]*ni_c[y]%mod*ni_c[x-y]%mod;
19 }
20 int poww(int x,int y){
21 int ans=1ll;
22 while(y){
23 if(y&1)ans=(ans*x)%mod;
24 x=(x*x)%mod;
25 y>>=1;
26 }
27 return ans%mod;
28 }
29 int ans=0;int kx[MAXN][MAXN];
30 int fir[MAXN][MAXN];int f[2][MAXN];int c[MAXN][MAXN];
31 void work1(){
32 memset(c,0,sizeof(c));
33 for(int k=1;k<=min(n,p);++k){ 34 for(int i=1;i<=min(n,p);++i){ 35 for(int j=1;j<=min(n,p);++j){ 36 c[i][j]=(c[i][j]+fir[i][k]*kx[k][j]%mod)%mod; 37 } 38 } 39 } 40 for(int i=1;i<=min(n,p);++i){ 41 for(int j=1;j<=min(n,p);++j) 42 fir[i][j]=c[i][j]%mod; 43 } 44 } 45 void work2(){ 46 memset(c,0,sizeof(c)); 47 for(int k=1;k<=min(n,p);++k){ 48 for(int i=1;i<=min(n,p);++i){ 49 for(int j=1;j<=min(n,p);++j){ 50 c[i][j]=(c[i][j]+kx[i][k]*kx[k][j]%mod)%mod; 51 } 52 } 53 } 54 for(int i=1;i<=min(n,p);++i){ 55 for(int j=1;j<=min(n,p);++j) 56 kx[i][j]=c[i][j]; 57 } 58 } 59 void ju_poww(int y){ 60 for(int i=1;i<=min(n,p);++i)fir[i][i]=1; 61 while(y){ 62 if(y&1){work1();} 63 work2(); 64 y>>=1;
65 }
66 }
67 signed main(){
68 n=read();m=read();p=read();q=read();
69 dp[0][0]=1;
70 for(int i=1;i<=n;++i){
71 for(int j=1;j<=min(p,i);++j){
72 dp[i][j]=(dp[i-1][j-1]*(p-(j-1)+mod)%mod+dp[i-1][j]*j%mod)%mod;
73 }
74 }
75 jie[0]=jie[1]=1;ni[1]=1;ni_c[0]=ni_c[1]=1;
76 for(int i=2;i<=p;++i){
77 jie[i]=(jie[i-1]*i)%mod;
78 ni[i]=((mod-mod/i)*ni[mod%i])%mod;
79 ni_c[i]=(ni[i]*ni_c[i-1])%mod;
80 }
81 for(int j=1;j<=p;++j){
82 f[0][j]=dp[n][j]%mod;
83 }
84 for(int j=1;j<=min(n,p);++j){
85 for(int k=1;k<=min(n,p);++k){
86 if(j+k<q)continue;
87 for(int x=max(j,max(q,k));x<=min(p,j+k);++x){
88 kx[j][k]=(kx[j][k]+C(p-j,x-j)%mod*C(j,k+j-x)%mod)%mod;
89 }
90 kx[j][k]%=mod;
91 kx[j][k]=kx[j][k]*poww(C(p,k)%mod,mod-2)%mod*dp[n][k]%mod;
92 kx[j][k]%=mod;
93 }
94 }
95 ju_poww(m-1);
96 for(int j=1;j<=p;++j){
97 for(int k=1;k<=p;++k){
98 f[1][j]=(f[1][j]+f[0][k]*fir[k][j]%mod)%mod;
99 }
100 }
101 int ans=0;
102 for(int j=1;j<=p;++j)ans=(ans+f[1][j])%mod;
103 printf("%lld\n",ans);
104 }

T3序列(数据结构乱搞)


好像是这套题里最简单的……

直接在树状数组里开vector每次查询时就lowerbound查出贡献

对于查询覆盖了一段区间的查询,我们在L,R+1处用两个树状数组标记后缀

然后对于询问覆盖x位置的值就用L的(1-x)的L贡献-(1-x)的R的贡献

1 #include
2 #define MAXN 410000
3 #define int long long
4 using namespace std;
5 vectorv[MAXN];vectorv1[MAXN];
6 int lowbit(int x){return x&(-x);}
7 int m,q,n,a[MAXN];
8 void add(int x,int k){
9 for(int i=x;i>=1;i-=lowbit(i)){
10 v[i].push_back(k);//printf("ps=%lld i=%lld\n",i,k);
11 }
12 }
13 void add1(int x,int k){
14 for(int i=x;i>=1;i-=lowbit(i)){
15 v1[i].push_back(k);//printf("ps=%lld i=%lld\n",i,k);
16 }
17 }
18 int anss[MAXN];
19 void pt(int x){
20 for(int i=0;i'9')c=getchar();
43 while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
44 return x;
45 }
46 int sum=0;
47 signed main(){
48 n=read();m=read();q=read();
49 for(int i=1;i<=n+1;++i){v[i].push_back(0);v[i].push_back(0x7fffffffffff);v1[i].push_back(0);v1[i].push_back(0x7fffffffffff);}
50 for(int i=1;i<=n;++i){
51 a[i]=read();
52 }
53 for(int i=1;i<=m;++i){
54 int l=read();int r=read();int xx=read();
55 add(l,xx);
56 add1(r+1,xx);
57 }
58 for(int i=1;i<=n+1;++i)sort(v[i].begin(),v[i].end()),sort(v1[i].begin(),v1[i].end());
59 for(int i=1;i<=n;++i){
60 anss[i]=(query(1,a[i])-query(i+1,a[i]));
61 anss[i]-=(query1(1,a[i])-query1(i+1,a[i]));
62 sum+=anss[i];
63 }
64 printf("%lld\n",sum);
65 for(int i=1;i<=q;++i){
66 int u=read();int v=read();
67 u^=sum;v^=sum;
68 sum-=anss[u];
69 anss[u]=query(1,v)-query(u+1,v);
70 anss[u]-=(query1(1,v)-query1(u+1,v));
71 sum+=anss[u];
72 a[u]=v;
73 printf("%lld\n",sum);
74 }
75 }
76 /*
77 4 2 2
78 1 4 2 3
79 2 4 3
80 1 3 2
81 6 6
82 2 7
83 */