題目大意
給定一個序列\(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