CSP-S全国模拟赛第三场 【nan死了】

mmt

居然第一步膜化乘除 都没看出来,没救了...

大概是贡献前缀和优化的做法

巨兔式讲解:大家都学会了么?

咱发现有大量的 (i/j , i%j ) 同时 对很多 c 产生了贡献,咱可以去优化这一部分的转移,具体做法就是根据前面能加的后面也能加,然后一路累加且算贡献

对于小于根号的所有 i/j ,咱可以优化这一部分转移,然后对于大于根号的 i/j ,暴力算就好了,两者复杂度都是是 n 根号 n 的

//by Judge
#include<bits/stdc++.h>
#define Rg register
#define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
#define ll long long
#define eps 1e-8
using namespace std;
const int mod=123456789;
const int M=1e5+3;
typedef int arr[M];
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[1<<21],*p1=buf,*p2=buf;
inline int read(){ int x=0,f=1; char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
} char sr[1<<21],z[20];int CCF=-1,Z;
inline void Ot(){fwrite(sr,1,CCF+1,stdout),CCF=-1;}
inline void print(int x,char chr='\n'){
	if(CCF>1<<20)Ot();if(x<0)sr[++CCF]=45,x=-x;
	while(z[++Z]=x%10+48,x/=10);
	while(sr[++CCF]=z[Z],--Z);sr[++CCF]=chr;
} int n,bl; arr a,b,c; double inv[M];
#define Div(x,y) (int(x*inv[y]+eps))
int main(){ n=read(),bl=sqrt(n);
	fp(i,1,n) inv[i]=1.0/i,a[i]=read(); fp(i,0,n-1) b[i]=read();
	fp(x,1,bl) fp(y,0,x-1){ Rg ll now=-1; Rg int A,l,r; // i/j = x 步长 , y 初始位置 
		for(Rg int k=x+y;k<=n;k+=x) if(Div(k,Div(k,x))==x){ // 必须是可达状态 ,此时 j = k/x 
			if(now<0) l=r=Div(k,x),A=k%r,now=1ll*a[x]*b[A]%mod;
			else{ ++r; if(Div(k,l)!=i) ++l; else A=k%l,now=(now+1ll*a[x]*b[A])%mod; }
			c[k]=(c[k]+now)%mod; // 当前的 now 存在的之前的状态当前 k 也可达 
		}
	}
	fp(i,1,n) fp(j,1,i) if(Div(i,j)<=bl) break;
		else c[i]=(c[i]+1ll*a[Div(i,j)]*b[i%j]%mod)%mod;
	fp(i,1,n) print(c[i]); return Ot(),0;
}

Sabotage

艹...这 T2 ,真 tm 长芝士了 ,支配树的新奇打法...

一眼看不出是支配树,只觉得题面眼熟,然后发现其实算法更加熟...

然后发现支配树代码很长,调挂了,于是康康题解区对比找找自己哪里挂了,于是有了这次惊人的发现

可以说是支配树板子题辣,这里的常规做法就不说了,又臭又长 (艹)

这里用的方法很诡(qiao)异(miao),大概就是说咱把这个 DAG 缩一下,对于入度大于 2 的节点把 Fa 设为 所有父亲节点的 LCA ,求解的时候输出两个点 LCA 的深度就好了,至于两个点不在同一棵树上(或者是别的无解情况),是可以被非常自然的处理掉的(雾),于是正确性就有了感性理解下的保证

然后咱可以发现这里的 LCA 是要随着拓扑的进行可以同时维护的...所以就打不了小常数的树剖了,于是顺便重温了一下倍增 LCA (雾)

具体康康代码咯?

卡了下常,进了 Rank1 (可能您康到咱的代码之后就不是了)

//by Judge
#include<cstdio>
#include<iostream>
#define Rg register
#define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(Rg int i=head[u],v=e[i].to;i;v=e[i=e[i].nxt].to)
using namespace std;
const int M=1e5+7;
typedef int arr[M];
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[1<<21],*p1=buf,*p2=buf;
inline int read(){ int x=0,f=1; char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
} char sr[1<<21],z[20];int C=-1,Z;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
inline void print(int x,char chr='\n'){
    if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]=chr;
} int n,m,pat,hd,tl,f[M][19]; arr head,lg,q,h,d;
struct Edge{ int to,nxt; }e[M<<1];
inline void add(int x,int y){ ++d[y];
	e[++pat]=(Edge){y,head[x]},head[x]=pat;
}
inline int LCA(int u,int v){ if(h[u]>h[v]) swap(u,v);
	for(Rg int i=0;h[v]-h[u];++i) if(((h[v]-h[u])>>i)&1) v=f[v][i]; if(u==v) return u;
	fd(i,17,0) if(f[u][i]^f[v][i]) u=f[u][i],v=f[v][i]; return *f[u];
}
int main(){ n=read(),m=read(),lg[0]=-1;
	Rg int x,y; fp(i,1,m) x=read(),y=read(),add(y,x);
	fp(i,1,n) lg[i]=lg[i>>1]+1,*f[i]=!d[i]?q[++tl]=i,0:-1;
	while(hd<tl){ x=q[++hd],h[x]=h[*f[x]]+1;
		fp(i,1,lg[h[x]]) f[x][i]=f[f[x][i-1]][i-1];
		go(x) *f[v]=*f[v]<0?x:LCA(x,*f[v]),!--d[v]&&(q[++tl]=v);
	} m=read(); while(m--) x=read(),y=read(),print(h[LCA(x,y)]);
	return Ot(),0;
}

LP

并没有什么特别的? 但是咱太菜了还是想不出来的那种

(看了题解后)不难想出 \(n*p^2\) 的 SB dp 方程 ,f[i][j][k]

看着数据范围应该是 np 的题目(NP-hard 可海星),那么怎么少掉一个 p ?

咱发现两个限制本质上是可以简化的,可以把 f 简化为 2 维的 f[i][j] ,j 表示对于选出的 b 之和小于等于 j 且 c 之和大于等于 j

然后两个转移方式(x 为 0 或 1)的 dp 转移,然后一个单调队列优化 x=1 时的转移就好了

空间继续优化,那么滚动可以解决,并且开数组变小了,代码常数却并没有怎么提高,所以隐隐起到了卡常的效果(空间小一般时间也会小,原因显而易见)

//by Judge
#include<cstdio>
#include<cstring>
#include<iostream>
#define Rg register
#define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
#define QwQ printf("IMPOSSIBLE!!!\n")
#define QvQ printf("%d\n",f[n&1][p])
using namespace std;
const int inf=2e9+7;
const int M=1e4+3;
typedef int arr[M];
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[1<<21],*p1=buf,*p2=buf;
inline bool cmin(int& a,int b){return a>b?a=b,1:0;}
inline int read(){ int x=0,f=1; char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
} int n,p,head,tail,x,y; arr a,b,c,f[2],q,w;
inline void Solv(){ n=read(),p=read(); fp(i,1,n) a[i]=read();
	fp(i,1,n) b[i]=read(); fp(i,1,n) c[i]=read(); fp(i,1,p) f[0][i]=inf;
	fp(i,1,n){ head=1,tail=0,x=i&1,y=x^1; fp(j,0,p) f[x][j]=f[y][j];
		fp(j,a[i],p){ while(head<=tail&&w[tail]>=f[y][j-a[i]]) --tail;
			while(head<=tail&&q[head]<j-b[i]) ++head;
			q[++tail]=j-a[i],w[tail]=f[y][q[tail]],cmin(f[x][j],w[head]+c[i]);
		}
	} f[n&1][p]<inf?QvQ:QwQ;
}
int main(){ int T=read();
	while(T--) Solv(); return 0;
}

明天继续吧...

posted @ 2019-10-21 16:12  Jμdge  阅读(206)  评论(0编辑  收藏  举报