题目大意
给定一个序列\(a_i\),求全部子区间,知足排序后是等差数列的个数
分析
首先须要一个快速的断定方法:
对于序列\(b_i\),若其排序后为等差数列,则必然有公差\(d=gcd(b_2−b_1,b_3−b_2,...)\) ,证实略去
因而问题转化为统计区间(l,r)知足:
$ max(a[l..r])−min(a[l..r])=(r−l) ∙|gcd(...)| \( 枚举r,能够在结合单调栈在线段树上维护max−min的值,处理区间修改操做 对于不一样的\)l,gcd\(的不一样分段只有\)log a$ 段,能够在l每次gcd 改变时在线段树上作单点修改
又$max(l,r)−min(l,r)−(r−l) ∙|gcd(...)|≥0 $,故能够统计线段树上的最小值及其出现的次数
就能获得区间内该表达式为0的个数node
题目大意
给定一个n×m的点阵,每次选两个相邻点连线
两我的轮流操做,不能连出封闭图形,不能操做者输
分析
不能连出封闭图形就是不能造成环,也就是图始终是一片森林
终态必定是一棵生成树,所以根据点数奇偶性便可判断ios
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return sum*f; } int main(){ int n=read(),m=read(); if(n>m)swap(n,m); if(n==1){ if(m&1==1)printf("NO"); else printf("YES"); } if(n==2){ printf("YES"); } if(n==3){ if(m==3)printf("NO"); else printf("YES"); } if(n==4){ printf("YES"); } return 0; }
签到模拟题c++
#include <bits/stdc++.h> #define ll long long #define int ll #define mod 100000000 #define N 100010 #define long_long_MAX 9187201950435737471 #define long_long_MIN -9187201950435737472 #define int_MAX 2139062143 #define int_MIN -2139062144 #define jh(x, y) (x ^= y ^= x ^= y) #define loc(x, y) ((x - 1) * m + y) #define lowbit(x) (x & -x) using namespace std; ll max(ll x,ll y){return x > y ? x : y;} ll min(ll x,ll y){return x > y ? y : x;} inline ll read() { ll a=0;ll f=0;char p=getchar(); while(!isdigit(p)){f|=p=='-';p=getchar();} while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();} return f?-a:a; } inline void print(ll x) { if(!x) return; if(x) print(x/10); putchar(x%10+'0'); } int t; signed main() { t = read(); while(t--) { int a1 = read(),b1 = read(),a2 = read(),b2 = read(); if(a1 > b1) swap(a1,b1); if(a2 > b2) swap(a2,b2); if((a1 == 2 && b1 == 8) || (a2 == 2 && b2 == 8)) { if(a1 == 2 && b1 == 8 && a2 == 2 && b2 == 8) printf("tie\n"); else if(a1 == 2 && b1 == 8) printf("first\n"); else printf("second\n"); } else if((a1 == b1) || (a2 == b2)) { if(a1 == b1 && a2 == b2) { if(a1 > a2) printf("first\n"); else if(a1 < a2) printf("second\n"); else printf("tie\n"); } else if(a1 == b1) printf("first\n"); else printf("second\n"); } else if(a1 != b1 && a2 != b2) { int x1 = (a1 + b1) % 10; int x2 = (a2 + b2) % 10; if(x1 > x2) printf("first\n"); else if(x1 < x2) printf("second\n"); else { if(b1 > b2) printf("first\n"); else if(b1 < b2) printf("second\n"); else printf("tie\n"); } } else printf("tie\n"); } system("pause"); return 0; }
题目大意
给定一棵树,每次从 s 出发,初始权值是 x ,过一条边消耗 w ,到一个点加$ a_i$
要求过程权值非负,不容许通过一个点 p ,求可达点个数
分析
离线进行点分治,每次求跨过点分治的根的可达点数目
能够预处理每一个点能不能到根,从根下去到这个须要多少初始权值
讨论p 所在位置,减去跨过p或本身子树内的部分
求子树内的答案能够先对于预处理的权值离散,
而后树状数组 + dfs 做差 进行维护
复杂度为 \(O(nlog^2n)\) ,常数不算太大
( 复古点分治题。。。git
题目大意
空间内有6个点,知足$|P_1A|≥k_1|P_1B|,|P_2C|≥k_2|P_2D| \( 求\)P_1\(,\)P_2$ 各自轨迹围成的空间体的体积交
分析
对于固定的\(k\),\(P_1\),\(P_2\) 的轨迹构成标准 阿波罗尼斯 球的球壳
对于\(≥k\) ,便是实心的球体,因而问题转化为求球的体积交
这个模板满世界都是,本身推的话,能够直接对于交部分的截面作面积的积分
大概是 \(∫π(r^2−x^2) dx\) 的形式,比较简单数组
#include<bits/stdc++.h> using namespace std; typedef long long ll; const double pi=acos(-1); const int MAX=100+10; const int inf=1e9+7; typedef struct{ double x,y,z,r; }Point; int n; Point a[MAX]; Point s,b,c,d,e; double dis(Point p,Point q){ double ans=sqrt((p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)+(p.z-q.z)*(p.z-q.z)); return ans; } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%lf%lf%lf", &b.x, &b.y, &b.z); scanf("%lf%lf%lf", &c.x, &c.y, &c.z); scanf("%lf%lf%lf", &d.x, &d.y, &d.z); scanf("%lf%lf%lf", &e.x, &e.y, &e.z); int k1,k2; scanf("%d%d",&k1,&k2); double r1=dis(b,c)/(k1+1)/(k1-1)*k1; double r2=dis(d,e)/(k2+1)/(k2-1)*k2; double ans=r1*r1*r1*pi*4/3+r2*r2*r2*pi*4/3; a[0].x=((k1*c.x+b.x)/(k1+1)+(k1*c.x-b.x)/(k1-1))/2; a[0].y=((k1*c.y+b.y)/(k1+1)+(k1*c.y-b.y)/(k1-1))/2; a[0].z=((k1*c.z+b.z)/(k1+1)+(k1*c.z-b.z)/(k1-1))/2; a[0].r=r1; s.x=((k2*e.x+d.x)/(k2+1)+(k2*e.x-d.x)/(k2-1))/2; s.y=((k2*e.y+d.y)/(k2+1)+(k2*e.y-d.y)/(k2-1))/2; s.z=((k2*e.z+d.z)/(k2+1)+(k2*e.z-d.z)/(k2-1))/2; s.r=r2; double d=dis(s,a[0]); if(s.r<a[0].r){ swap(s.r,a[0].r); swap(s.x,a[0].x); swap(s.y,a[0].y); swap(s.z,a[0].z); } if(a[0].x==s.x&&a[0].y==s.y&&a[0].z==s.z){ printf("%.3lf\n",ans-s.r*s.r*s.r*4.0*pi/3.0); continue; } double tmp=0; if(d>=s.r+a[0].r){ printf("%.3lf\n",ans-(a[0].r*a[0].r*a[0].r+s.r*s.r*s.r)*4.0*pi/3.0); continue; } else if(d+a[0].r<=s.r){ tmp+=(4.0/3.0)*pi*a[0].r*a[0].r*a[0].r; } else { double co=(s.r*s.r+d*d-a[0].r*a[0].r)/(2.0*d*s.r); double h=s.r*(1.0-co); tmp+=(1.0/3.0)*pi*(3.0*s.r-h)*h*h; co=(a[0].r*a[0].r+d*d-s.r*s.r)/(2.0*d*a[0].r); h=a[0].r*(1.0-co); tmp+=(1.0/3.0)*pi*(3.0*a[0].r-h)*h*h; } printf("%.3lf\n",ans-((a[0].r*a[0].r*a[0].r+s.r*s.r*s.r)*4.0*pi/3.0-tmp)); } return 0; }
题目大意
给定n个区间,要求将它们分红k组,每组之间有交,最大化每组交长度之和
分析
首先考虑简化问题,对于每个包含其它区间的大区间,其最优决策有两种
1.归属到一个被它包含的区间所在的组,不影响答案
2.独自一组,长度直接算入答案
剩下的部分均是独立的小区间,不妨提取出来为$(a_i,b_i) \(,排序以后进行dp 令\)dp_{i,j}$表示前i个,分了j组的方案数,转移为
$dp_{i,j} + b_i− a_k→ dp_{k,j}+1 \(,且须要知足\)b_i> a_k \(,容易用单调队列优化 求得\)dp_{n’,i} \(后再综合大区间的贡献便可 复杂度为\)O(n^2) \(,常数较小。应该故意放过了带\)log $的作法。ide
广搜模拟题优化
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> using namespace std; char a[22][22],b[22][22]; int x[4]={1,0,0,-1}; int y[4]={0,-1,1,0}; char step[4]={'D','L','R','U'}; struct node{ int ax,ay,bx,by,step; node (int AX,int AY,int BX,int BY,int S){ ax=AX,ay=AY,bx=BX,by=BY,step=S; } }; string S[22][22][22][22]; bool vis[22][22][22][22]; queue<node> q; void bfs(){ q.push(node(20,20,20,1,0)); vis[20][20][20][1]=1; while(!q.empty()){ node now=q.front(); if(now.ax==1&&now.ay==20&&now.bx==1&&now.by==1)break; q.pop(); for(int i=0;i<=3;i++){ int AX=now.ax+x[i],AY=now.ay+y[i],BX=now.bx+x[i],BY=now.by-y[i]; if(a[AX][AY]!='.'&&b[BX][BY]!='.')continue; if(b[BX][BY]=='.'&&a[AX][AY]=='.'){ if(vis[AX][AY][BX][BY]==1)continue; q.push(node(AX,AY,BX,BY,now.step+1)); vis[AX][AY][BX][BY]=1; S[AX][AY][BX][BY]=S[now.ax][now.ay][now.bx][now.by]; if(i==0)S[AX][AY][BX][BY]+="D"; if(i==1)S[AX][AY][BX][BY]+="L"; if(i==2)S[AX][AY][BX][BY]+="R"; if(i==3)S[AX][AY][BX][BY]+="U"; } else if(a[AX][AY]!='.'){ if(vis[now.ax][now.ay][BX][BY]==1)continue; q.push(node(now.ax,now.ay,BX,BY,now.step+1)); vis[now.ax][now.ay][BX][BY]=1; S[now.ax][now.ay][BX][BY]=S[now.ax][now.ay][now.bx][now.by]; if(i==0)S[now.ax][now.ay][BX][BY]+="D"; if(i==1)S[now.ax][now.ay][BX][BY]+="L"; if(i==2)S[now.ax][now.ay][BX][BY]+="R"; if(i==3)S[now.ax][now.ay][BX][BY]+="U"; } else{ if(vis[AX][AY][now.bx][now.by]==1)continue; q.push(node(AX,AY,now.bx,now.by,now.step+1)); vis[AX][AY][now.bx][now.by]=1; S[AX][AY][now.bx][now.by]=S[now.ax][now.ay][now.bx][now.by]; if(i==0)S[AX][AY][now.bx][now.by]+="D"; if(i==1)S[AX][AY][now.bx][now.by]+="L"; if(i==2)S[AX][AY][now.bx][now.by]+="R"; if(i==3)S[AX][AY][now.bx][now.by]+="U"; } } } } int main(){ //freopen("i.in","r",stdin); //freopen("i.out","w",stdout); for(int i=1;i<=20;i++){ for(int j=1;j<=20;j++) a[i][j]=getchar(); getchar(); for(int j=1;j<=20;j++) b[i][j]=getchar(); getchar(); } bfs(); cout<<q.front().step<<endl; int len=S[1][20][1][1].size(); int ax=20,ay=20,bx=20,by=1; a[ax][ay]='A';b[bx][by]='A'; cout<<S[1][20][1][1]; cout<<endl; for(int i=0;i<len;i++){ char now=S[1][20][1][1][i]; int step; if(now=='D')step=0; if(now=='L')step=1; if(now=='R')step=2; if(now=='U')step=3; if(a[ax+x[step]][ay+y[step]]=='.' || a[ax+x[step]][ay+y[step]]=='A')ax=ax+x[step],ay=ay+y[step]; if(b[bx+x[step]][by-y[step]]=='.' || b[bx+x[step]][by-y[step]]=='A')bx=bx+x[step],by=by-y[step]; a[ax][ay]=b[bx][by]='A'; } for(int i=1;i<=20;i++){ for(int j=1;j<=20;j++) cout<<a[i][j]; cout<<" "; for(int j=1;j<=20;j++) cout<<b[i][j]; cout<<endl; } //system("pause"); return 0; }
题目大意
给定一个集合,求其全部大小为k的子集的gcd之积
分析
考虑分质因子统计,计算\(f_{p,c}\)表示至少包含$p^c \(的数个数,方案数为\)(\frac{f_{p,c}}k)\(,直接对于差分累和便可 注意须要计算获得的其实是指数,所以咱们须要对于\)φ(P)\(取模 经过分解质因数暴力求φ(P)便可,只须要预处理\)√P \(内的质数,复杂度为\)O(√P+T\frac{√P}{log P})$
\(φ(P)\)不是质数,可是鉴于k较小,组合数直接\(O(nk)\)递推便可(为此 n 开得比较小)
容斥须要 \(O(xlog x)\)的时间,可是没有乘法,常数不大
最后统计答案还须要\(O(\frac{x}{log x})\)次快速幂,模乘法须要快速乘,可是有int128
最后统计答案部分复杂度为\(O(x)\)
统计每一个数倍数的个数,若是对于每一个读入的\(x\)分解,复杂度为\(|S|√x\),理应不能经过
用ln级别的累和则能够,并且实际上只须要对于每一个质因子幂次进行计算
(其实我不会算复杂度,反正不是满的ln)
std在使用上述的暴力分解P的状况下依然表现良好 (500ms ~)
可是写很差可能依然有点卡常,若是卡的话建议直接换\(Pollard’s Rho\)质因数分解,说不定快不少ui
题目大意
已知若干时刻的单调栈大小,构造一个合法的序列
分析
对于没有给定b的位置,必定往直接单调栈插入元素
不然根据给定的单调栈大小判断弹掉栈顶的几个元素,若是不够就无解
每次根据被弹掉的最后一个元素,以及没有弹掉的栈顶元素
由此,构造出一组拓扑关系
而后直接作一次拓扑排序便可构造出一组合法解
在选手代码中也看到了直接用list 维护的spa
#include<bits/stdc++.h> using namespace std; const int M=1e6+5; void Rd(int &r) { static char c; for(c=0;c<'0' || c>'9';c=getchar()); for(r=0;c>='0' && c<='9';c=getchar())r=r*10+(c^'0'); } int n,m; int Q[M]; int L[M],R[M],I[M];void Link(int l,int r){L[r]=l,R[l]=r;} int Stk[M]; void Solve() { Link(0,n+1); int pos=1,sv=0; for(int i=1;i<=n;i++) { if(Q[i]==-327327327)continue; if(Q[i]<1 || Q[i]>i){puts("-1");return;} if(Q[i]-sv > i-pos+1){puts("-1");return;} while(pos<=i) { if(sv<Q[i]) { Link(pos,R[Stk[sv]]),Link(Stk[sv],pos); sv++,Stk[sv]=pos; } else { sv=Q[i]-1; Link(pos,R[Stk[sv]]),Link(Stk[sv],pos); Stk[++sv]=pos; } pos++; } } while(pos<=n)Link(pos,R[Stk[sv]]),Link(Stk[sv],pos),pos++; for(int i=R[0],cnt=1;i<=n;i=R[i],cnt++)I[i]=cnt; for(int i=1;i<=n;i++)printf("%d ",I[i]); } int main() { Rd(n);Rd(m);if(m>n)return 0; for(int i=1;i<=n;i++)Q[i]=-327327327; for(int i=1,x;i<=m;i++) { Rd(x),Rd(Q[x]); if(x<1 || x>n || Q[x]<1 || Q[x]>n)return 0; } Solve(); system("pause"); return 0; }
题目大意
给定n我的之间的好友关系,每次单点增长一我的的步数
求每一个人在本身列表里保持冠军的时间
分析
设界值 S=√n ,权值值域W=10000
按照S分块考虑点,称度数≤S的点为小点,度数>S的点为大点
依次考虑每个时刻的事件,维护每一个人成为冠军的区间
假设被修改的点为x ,容易分红若干状况
1.若是原先x是冠军,无需修改冠军状况
2.x不是冠军,设原先权值为a,新权值为b,a<b
2-1.x是小点
直接枚举更新全部和x相邻的点,更新它们的冠军状况,同时也就知道了x是否成为了冠军
2-2.x是大点
2-2-1. 大点周围的大点
直接枚举而后更新便可
2-2-2.大点周围的小点
注意到W较小,所以能够直接暴力存下和x相邻的 小点 中,权值为w的且为冠军的点的集合\(C_x\),w
成为冠军的时间只有在每次小点被增长才会出现,C的总元素个数为O(nS)
暴力扫描权值,判断i∈(a,b],\(C_x\),i 中的点是否再也不成为冠军 ,容易发现无需维护C的删除操做
元素和集合不会被重复扫描, 所以复杂度为O(SW+nS)
容易发现W无限制时也彻底可写,可是据出题人称这样很是方便code