.
LOJ的std+数据+当时讲题的记忆硬是刚了五道题(D1t3咕咕咕)
稍微写一下题解
LOJ6432 「PKUSC2018」真实排名
一道比较简单的题,不过需要注意很多细节
考虑排名不变的两种情况
1.自己分数 x x x不变,那么所有分数在 [ [ x 2 ] , x ) [[\frac{x}{2}],x) [[2x],x)中的其他人的分数也不能变,否则必然有在自己后面的人到自己前面
2.自己分数 x x x翻倍,那么所有分数在 [ x , 2 x ) [x,2x) [x,2x)中的人分数必须翻倍,其他人则可以随意
预处理一下组合数,让后二分找范围就可以了
需要特判0
#include
#include
#include
#define N 100010
#define M 998244353
#define LL long long
using namespace std;
int n,m,v[N],w[N]; LL js[N],inv[N];
inline LL pow(LL x,LL k,LL s=1){
for(;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0; return s;
}
inline LL C(int n,int m){
if(m<0) return 0;
if(n<m) return 0;
return js[n]*inv[m]%M*inv[n-m]%M;
}
inline int rank(int x){
return lower_bound(w+1,w+1+n,x)-w;
}
inline int count(int l,int r){
if(l>r) return 0;
int x=rank(l),y=rank(r+1);
while(x&&w[x]>=l) --x;
return y-x-1;
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",v+i);
memcpy(w,v,sizeof w); sort(w+1,w+1+n);
for(int i=*js=1;i<=n;++i) js[i]=js[i-1]*i%M;
inv[n]=pow(js[n],M-2);
for(int i=n;i;--i) inv[i-1]=inv[i]*i%M;
for(int i=1;i<=n;++i){
int r=count(v[i]+1>>1,v[i]-1)+1;
int l=count(v[i],(v[i]<<1)-1);
if(!v[i]) ++l;
printf("%lld\n",(C(n-r,m)+C(n-l,m-l))%M);
}
}
.
LOJ6433 「PKUSC2018」最大前缀和
很好的DP题,当时只水了一个 O ( 3 n ) O(3^n) O(3n)的状压
考虑最大(非空)前缀和的性质,假设这个位置是 x x x,很容易发现
1. ∑ i = x + 1 r a i < 0 \sum_{i=x+1}^ra_i<0 ∑i=x+1rai<0 这里 r ≤ n r\le n r≤n
2. ∑ i = l x a i > 0 \sum_{i=l}^xa_i > 0 ∑i=lxai>0 这里 1 < l ≤ x 1<l\le x 1<l≤x
为什么l要大于1,因为答案要求是非空,也就是说即使 ∑ i = 1 x < 0 \sum_{i=1}^x<0 ∑i=1x<0也不可以取到0
让后,我们就可以根据这两条限制来做状压DP
设 F i F_i Fi表示取用集合i的元素,能组成多少种排列满足条件2(任何一个后缀和大于0)
设 G i G_i Gi表示取用集合i的元素,能组成多少种排列满足条件1(任何一个前缀和小于0)
预处理 S i S_i Si表示集合i的元素之和
考虑转移,对于 F F F,枚举下一个加入的元素即可,需要保证当前状态和大于0才能转移
对于 G G G,枚举最后加入的元素是哪一个转移,需要保证当前状态和小于0才能转移
答案是 ∑ i ∈ S F i ∗ S i ∗ G S x o r i \sum_{i\in S}F_i*S_i*G_{S\ xor \ i} ∑i∈SFi∗Si∗GS xor i
#include
#include
#include
#define N 20
#define M 998244353
#define LL long long
using namespace std;
int n; LL s[1<<N],f[1<<N],g[1<<N],A;
inline void ad(LL& x,LL y){ x=(x+y)%M; }
int main(){
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%lld",s+(1<<i));
f[1<<i]=1;
}
int m=(1<<n)-1;
for(int i=1;i<=m;++i)
s[i]=s[i&i-1]+s[i&-i];
for(int i=1;i<=m;++i) if(s[i]>0)
for(int j=m^i;j;j&=j-1) ad(f[i|j&-j],f[i]);
*g=1;
for(int i=1;i<=m;++i) if(s[i]<=0)
for(int j=i;j;j&=j-1) ad(g[i],g[i^j&-j]);
for(int i=1;i<=m;++i) ad(A,f[i]*s[i]%M*g[i^m]%M);
printf("%lld\n",(A+M)%M);
}
LOJ6434咕咕咕
LOJ6435 「PKUSC2018」星际穿越
居然是倍增?还有这种操作?!
一个非常牛逼的解法,首先稍加观察就可以发现,往后走的次数不多于1
而且询问总是在起点的前面
那么就可以考虑利用建树让后倍增的做法来求
首先,我们记 F i F_i Fi表示从i的后面任何一个节点除法,一步能走到的最前面的那个节点
这也算是在树上的父亲节点,处理方法就是对 L i L_i Li做一次后缀最小值即可
让后,预处理出 S i S_i Si表示从节点i出发,到1~i-1的所有点的最短路线中,不计往后走的那一步的路程之和
关于为什么不计往后走的那一步,稍后再解释
那么显然 S i = S F i + i − 1 , S 1 = 0 S_i=S_{F_i}+i-1,S_1=0 Si=SFi+i−1,S1=0
接下来考虑如何求答案
考虑差分,我们将 [ l , r ] [l,r] [l,r]拆成两个区间答案的差
让后考虑计算节点 x x x到 [ 1 , r ] [1,r] [1,r]的距离之和
首先,如果 L x < r L_x<r Lx<r就说明,从 x x x出发可以一步到达r,那么所有在 L x L_x Lx之前的节点,除了第一步,剩下要走的步数之和,就正好是 S L x S_{L_x} SLx
为什么?考虑从 L x L_x Lx出发走到一个节点i的路线,要么就是一直向前,要么就是往后走了一步,而如果,从 L x L_x Lx可以往后一步走到,那么从 x x x出发,也肯定可以一步走到
所以,可以得到答案为 S L x + r S_{L_x}+r SLx+r
如果 r < L x r<L_x r<Lx,那么就考虑走 F x F_x Fx,我们通过倍增计算出至少要走几步才能走到一个比 r r r要前的位置,设走了 t t t步,走到了 y y y且 y < = r y<=r y<=r
那么答案就是 ( t + 1 ) ∗ r + S y (t+1)*r+S_y (t+1)∗r+Sy
首先,由 x x x到 F x F_x Fx相当于往后走一步,再往前走一步,需要2步,但是为什么不是 2 t 2t 2t而是 t + 1 t+1 t+1?因为向后走的步数,只需要最多1步,往后走过一次,如果已经到达过x之后的节点,再从x走到 F x F_x Fx就只需要1步,这样就可以完美解决本题了
#include
#include
#include
#define N 300010
#define LL long long
using namespace std;
int n,m,f[18][N],l[N]; LL S[N];
inline int gcd(int x,int y){
while(y^=x^=y^=x%=y); return x;
}
inline LL gf(int x,int y){
if(!x) return 0;
int A=1; y=l[y];
if(x<y){
++A;
for(int i=17;~i;--i)
if(x<f[i][y]) y=f[i][y],A+=(1<<i);
y=f[0][y];
}
return A*x+S[y];
}
int main(){
scanf("%d",&n); f[0][n+1]=n;
for(int i=2;i<=n;++i) scanf("%d",l+i);
for(int i=n;i>1;--i) f[0][i]=min(l[i],f[0][i+1]);
for(int i=1;i<18;++i)
for(int j=1;j<=n;++j)
f[i][j]=f[i-1][f[i-1][j]];
for(int i=2;i<=n;++i) S[i]=S[f[0][i]]+i-1;
scanf("%d",&m);
for(int l,r,x;m--;){
scanf("%d%d%d",&l,&r,&x);
LL A=gf(r,x)-gf(l-1,x); l=r-l+1; r=gcd(A%l,l);
printf("%lld/%d\n",A/r,l/r);
}
}
.
LOJ6436 「PKUSC2018」神仙的游戏
口算什么的不存在的
以后见到01字符串匹配类似的,都可以往FFT这方面去考虑
我们设 A i = [ s i = 1 ] , B i = [ s n − i − 1 = 0 ] A_i=[s_i=1],B_i=[s_{n-i-1}=0] Ai=[si=1],Bi=[sn−i−1=0]
那么求一个A和B的卷积 C k = ∑ i = 0 k A i ∗ B k − i C_k=\sum_{i=0}^kA_i*B_{k-i} Ck=∑i=0kAi∗Bk−i
发现,当 C k = 0 C_k=0 Ck=0时,k可能为一个border
为什么是可能?参考样例,简单来说,同一个?不能变成两个不同的数字
于是利用另外一个性质,如果 x x x为周期,那么 k x kx kx为周期,k为正整数
枚举倍数判断一下就好了
#include
#include
#include
#define N 1100000
#define M 998244353
#define LL long long
using namespace std;
char s[N]; int vis[N];
int n,m,W[N],iW[N],A[N],B[N];
inline LL pow(LL x,LL k,LL s=1){
for(;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0; return s;
}
inline void init(){
for(int j=1;j<N;j<<=1){
W[j]=pow(3,(M-1)/j);
iW[j]=pow(W[j],M-2);
}
}
inline void NTT(int* A,int n,int g){
static int b[N]; memcpy(b,A,n<<2);
for(int i=0,j=0;i<n;++i){
if(i>j) swap(b[i],b[j]);
for(int l=n>>1;(j^=l)<l;l>>=1);
}
for(int k=1;k<n;k<<=1){
LL w=g?W[k<<1]:iW[k<<1],u,v,z=1;
for(int j=k;j<n;++j&k?z=z*w%M:z=1,j|=k){
u=b[j^k]; v=z*b[j]%M;
b[j^k]=(u+v)%M; b[j]=(u-v+M)%M;
}
}
if(!g){
g=pow(n,M-2);
for(int i=0;i<n;++i) A[i]=(LL)b[i]*g%M;
} else memcpy(A,b,n<<2);
}
int main(){
scanf("%s",s);
init(); n=strlen(s);
for(int i=0;i<n;++i){
A[i]=s[i]=='0';
B[i]=s[n-i-1]=='1';
}
for(m=1;m<n+n;m<<=1);
NTT(A,m,1);
NTT(B,m,1);
for(int i=0;i<m;++i) A[i]=(LL)A[i]*B[i]%M;
NTT(A,m,0);
for(int i=0;i<m;++i) vis[abs(n-i-1)]+=A[i];
LL A=0;
for(int i=1;i<n;++i){
bool f=0;
for(int j=n-i;j<=n;j+=n-i)
if(vis[j]){ f=1; break; }
if(!f) A^=(LL)i*i;
}
A^=(LL)n*n;
printf("%lld\n",A);
}
.
LOJ6437 「PKUSC2018」PKUSC
被精度卡哇!了
参考 jefflyy \text{jefflyy} jefflyy的博客,我们可以发现一个显然的解法
每个敌人画圆,和多边形求交,弧长之和除以2π即可
因为我的做法,导致精度出现巨大问题(全程都在用方程算而不是角度,就连弧中点也是拿复数开方)
最后不得不对着数据调了一天
#include
#include
#include
#include
#define eps 1e-6
#define db double
#define N 1010
#define pi 3.14159265358979323846
#define pi2 6.283185307179586476925286766559
using namespace std;
inline int sgn(db x){
return fabs(x)<eps?0:(x>0?1:-1);
}
struct vec{ db x,y; };
typedef vec point;
inline bool operator==(vec a,vec b){ return a.x==b.x&&a.y==b.y; }
inline bool operator!=(vec a,vec b){ return sgn(a.x-b.x)||sgn(a.y-b.y); }
inline bool cx(point a,point b){ return a.x<b.x; }
inline bool ca(point a,point b){
if(a.y*b.y<0) return sgn(a.y)<sgn(b.y);
return sgn(a.y)*(a.x-b.x)>0;
}
inline db dot(vec a,vec b){ return a.x*b.x+a.y*b.y; }
inline db crs(vec a,vec b){ return a.x*b.y-a.y*b.x; }
inline db l2(vec a){ return dot(a,a); }
inline vec operator+(vec a,vec b){ return (vec){a.x+b.x,a.y+b.y}; }
inline vec operator-(vec a,vec b){ return (vec){a.x-b.x,a.y-b.y}; }
inline vec operator*(vec a,db x){ return (vec){a.x*x,a.y*x}; }
inline vec operator/(vec a,db x){ return (vec){a.x/x,a.y/x}; }
inline vec operator*(vec a,vec b){ return (vec){a.x*b.x-a.y*b.y,a.y*b.x+a.x*b.y}; }
inline vec sqrt(vec x,vec f){
db a=x.x,b=x.y;
b=b*b/4;
//u-v=a,uv=b
db c=sqrt(a*a+4*b);
db u=(c+a)/2,v=(c-a)/2;
u=sqrt(u); v=sqrt(v);
if(sgn(u*v)!=sgn(x.y)) v=-v;
vec y=(vec){u,v};
if(crs(f,y)<0) y=(vec){-u,-v};
return y;
}
inline db ang(vec x){ return atan2(x.y,x.x); }
inline int cross(point a,point A,point b,point B){
return sgn(crs(A-a,b-a))*sgn(crs(A-a,B-a))<0
&& sgn(crs(B-b,a-b))*sgn(crs(B-b,A-b))<0;
}
inline int on(point x,point a,point b){
if(a.x>b.x) swap(a,b);
return (a.x<x.x||fabs(a.x-x.x)<1e-2)
&& (x.x<b.x||fabs(x.x-b.x)<1e-2);//1e-5会WA,点积判断会WA
}
point p[N],w[N],s[N];
int n,m,c;
inline void intersection(point t,point a,point b){
db x,y;
db A=(a-b).y,B=-(a-b).x,C=A*a.x+B*a.y,r2=dot(t,t);
if(!sgn(A)){
y=C/B;
if(sgn(r2-y*y)<0) return;
x=sqrt(r2-y*y);
if(on((vec){x,y},a,b)) w[++c]=(vec){x,y};
x=-x;
if(on((vec){x,y},a,b)) w[++c]=(vec){x,y};
} else if(!sgn(B)){
x=C/A;
if(sgn(r2-x*x)<0) return;
y=sqrt(r2-x*x);
if(on((vec){x,y},a,b)) w[++c]=(vec){x,y};
y=-y;
if(on((vec){x,y},a,b)) w[++c]=(vec){x,y};
} else {
db dA=A*A+B*B,dB=-2*A*C,dC=C*C-r2*B*B;
db dlt=dB*dB-4*dA*dC;
if(dlt<0) return;
x=(-dB+sqrt(dlt))/(dA*2.);
y=(C-A*x)/B;
if(on((vec){x,y},a,b)) w[++c]=(vec){x,y};
x=(-dB-sqrt(dlt))/(dA*2.);
y=(C-A*x)/B;
if(on((vec){x,y},a,b)) w[++c]=(vec){x,y};
}
}
inline int in(point t){
int A=0;
point o=(vec){1.3893e6,3.38756e6};
for(int i=1;i<=m;++i)
if(cross(o,t,s[i],s[i+1])) ++A;
return A&1;
}
inline db ra(db x){ if(x<-pi) return x+pi2; return x; }
int main(){
// freopen("1.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%lf%lf",&p[i].x,&p[i].y);
for(int i=1;i<=m;++i) scanf("%lf%lf",&s[i].x,&s[i].y);
s[m+1]=s[1]; db A=0;
for(int i=1;i<=n;++i){
c=0;
for(int j=1;j<=m;++j)
intersection(p[i],s[j],s[j+1]);
// for(int j=1;j<=c;++j) printf("%lf\n",ang(w[j])-ang(p[i]));
if(c<2){
if(in(p[i])) A+=1.;
} else {
sort(w+1,w+1+c,ca);
c=unique(w+1,w+1+c)-w-1;
w[c+1]=w[1];
for(int j=1;j<c;++j)
if(in(sqrt(w[j]*w[j+1],w[j]))){
db t=ang(w[j+1])-ang(w[j]);
if(t<-1e-2) t+=pi2;//这里WA得我也是醉了
A+=t/pi2;
}
if(in(sqrt(w[c]*w[c+1],w[c]))){
db t=ang(w[c+1])-ang(w[c]);
if(t<0) t+=pi2;
A+=t/pi2;
}
}
}
printf("%.5lf\n",A);
}
大概就是这些,应该不会再更新了