csp-s模拟测试41「夜莺与玫瑰·玫瑰花精·影子」
夜鶯與玫瑰
題解
聯賽$T1$莫比烏斯$\%\%\%$
?
?$dead$? $line$是直線
首先橫豎就是$n+m$這比較顯然
枚舉方向向量
首先我們枚舉方向向量時只枚舉右下方向,顯然貢獻$*2$就是所有斜著的直線
$i,j$表示當自己向右$i$個單位長度,向下$j$單位長度
我們相同斜率下只算最短的線貢獻,(因為其他長度下方案數都包含在最短里面了)
我們方向向量$i$,$j$的$gcd(i,j)==1$時我們枚舉的才是當前斜率最短長度,
然后考慮貢獻
考慮容斥,先算出來當前長度下所有線段再減去重合的
$(n-a)*(m-b)$是總方案數,考慮重合部分
假設我們有一個4*4點陣
. . . .
. . . .
. . . .
. . . .
我們算1,1方向向量貢獻
\ \ \ .
\ \ \ \
\ \ \ \
. \ \ \
只有
\ \ \ .
\ \ \ \
\ \ \ \
. \ \ \
標藍才有貢獻,別的都是算重的
定義前趨為$x-1$ $y-1$,后繼$x+1$ $y+1$
觀察這些線發現符合條件就是前趨不在點陣而后繼在點陣數量
例如$1$,$1$這個點$+$方向向量得到$-1$ $-1$ 和$2$ $2$
因為$-1$ $-1$不在點陣內所以是合法的
,我們把他們都提到與邊界相重
看他們相減后是否在邊界中即可
重復的部分就是$max((n-2*a),0)*max((m-2*b),0)$
$\sum\limits_ {a=1}^{a<=n} \sum\limits_{b=1}^{b<=m} [gcd(a,b)==1](n-a)*(m-b)-max((n-2*a),0)*max((m-2*b),0)$
這樣我們還是$AC$不了$T=10000$稍巨
所以我們預處理一下,讓查詢變成$O(n)$的$(其實可以是O(1))然而出題人還卡空間$
把原式子拆成$(n-a)*m-(n-a)*b$每一個$gcd(a,b)==1$都會對第一個式子造成貢獻,而后面那個式子就是$(n-a)*{\sum\limits_{b=1}^{b<=m} [gcd(a,b)==1] b}$
維護前綴和$tot(a,m)$表示$b=1--m所有數中$與$a$,$gcd==1$的個數和為$tot(a,m)$,
$sum(a,m)$表示$b=1--m$中所有$gcd(a,b)==1$對應$\sum\limits_{b=1}^{b<=m} [gcd(a,b)==1] b$和為$sum(a,m)$
所以式子$\sum\limits_ {a=1}^{a<=n} \sum\limits_{b=1}^{b<=m} [gcd(a,b)==1](n-a)*(m-b)$可以化為$(n-a)*(tot(a,m)*m-sum(a,m))$
后面這個式子類似
首先如果$(n-2*a)<=0$或$(m-2*b)<=0$就不用減了
所以$m-2*b>0$才可以即$b<\frac{m}{2}$
所以后面式子可以化為$(n-2*a)*(tot(a,\frac{m}{2})*m-2*sum(a,\frac{m}{2}))$
總式子就是$\sum\limits_{a=1}^{a<=n} (n-a)*(tot(a,m)*m-sum(a,m))-(n-2*a)*(tot(a,\frac{m}{2})*m-2*sum(a,\frac{m}{2}))$
單單是這樣你還是$AC$不了$4000*4000$枚舉$gcd$很慢,你需要遞推
代碼
#include<iostream> #include<cstdio> using namespace std; int T,n,m,sum[4100][4100]; const int mod=1073741824; short num[4100][4100],g[4100][4100]; int gcd(int a,int b){return b?gcd(b,a%b):a;} int main(){for(register int i=1;i<=4001;i++){g[i][i]=g[0][i]=g[i][0]=i;for(register int j=1;j<i;j++){g[i][j]=g[j][i]=g[j][i%j];}}for(register int i=1;i<=4001;i++){for(register int j=1;j<=4001;j++){sum[i][j]=sum[i][j-1];num[i][j]=num[i][j-1];if(g[i][j]==1) sum[i][j]=(sum[i][j]+j)%mod,num[i][j]++;}}scanf("%d",&T);while(T--){scanf("%d%d",&n,&m);long long ans=0;for(register int i=1;i<n;i++){ans=(ans+(num[i][m]*m%mod-sum[i][m]%mod)*(n-i)%mod)%mod;if(2*i<n) ans=ans-(num[i][m/2]*m%mod-2*sum[i][m/2]%mod)*(n-i*2)%mod;}ans=(ans*2%mod+n+m)%mod;printf("%lld\n",(ans+mod)%mod);} } View Code夜鶯
玫瑰花精
題解
抱歉,題解沒時間寫了
對于 100%的數據 可以考慮線段樹。首先我們對區間[1..n]建立一棵線段樹。對于每一個節點, 維護 4 個值。分別是 l,r,mid,p。l 表示在當前結點線段樹所在區間最左邊的花精所在的位置,r 表示最右邊的花精所在的位置。mid 表示在這個小區間[l,r]中的 兩只花精之間的最長距離除以 2 后的值。p 表示取 mid 值時所在的緊鄰的兩只 花精的中間位置,也就是在[l,r]中的答案值。 對于 1 詢問:訪問線段樹的第一個節點,我們比較 l-1,n-r,mid 的值哪 個更大,就選哪個,它們的答案依次是 1,n,p。假設我們求得的位置是 fairy[x]。 然后訪問[fairy[x],fairy[x]]所在的線段樹的葉子節點,初始化它的值,然后回溯, 進行合并。對于 tr[x].l 與 tr[x].r 可以通過兩個兒子的 l,r 信息得出。對于 tr[x].mid 值,首先在左右兒子的 mid 值中去一個最大的值。其次考慮一種情況,就是夾在 兩個線段之間的距離,可以通過(tr[x+x+1].l-tr[x+x].r) / 2 的值得出在于 mid 進行比較,然后 p 就隨著 mid 的值的更新而更新。 對于 2 詢問:訪問詢問花精所在的位置,直接將它的葉子節點 [fairy[x],fairy[x]]刪除,然后回溯時,再做一次合并操作。 View Code代碼
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 1010101 struct tree{ll l,r,mid,ql,qr,p; }tr[A]; ll n,m; ll in[A]; void built(ll x,ll l,ll r){tr[x].l=l,tr[x].r=r;if(l==r){return ;}ll mid=(l+r)>>1;built(x<<1,l,mid);built(x<<1|1,mid+1,r); } void update(ll x){ // printf("ql=%lld qr=%lld\n",tr[x].ql,tr[x].qr);if(tr[x<<1].ql) tr[x].ql=tr[x<<1].ql;else tr[x].ql=tr[x<<1|1].ql;if(tr[x<<1|1].qr) tr[x].qr=tr[x<<1|1].qr;else tr[x].qr=tr[x<<1].qr;tr[x].mid=tr[x<<1].mid;tr[x].p=tr[x<<1].p;if(tr[x<<1].qr&&tr[x<<1|1].ql){ // ll minn=tr[x].mid;if((tr[x<<1|1].ql-tr[x<<1].qr)/2>tr[x].mid){tr[x].mid=(tr[x<<1|1].ql-tr[x<<1].qr)>>1;tr[x].p=(tr[x<<1].qr+tr[x<<1|1].ql)>>1; // minn=tr[x].mid; }if(tr[x<<1|1].mid>tr[x].mid){tr[x].mid=tr[x<<1|1].mid;tr[x].p=(tr[x<<1|1].p); // minn=tr[x].mid; }} // printf("x=%lld l%lld--r%lld <<1%lld %lld |1%lld %lld區間 leftson mid=%lld mid=%lld x.mid=%lld p=%lld p=%lld p=%lld \n",x,tr[x].ql,tr[x].qr,tr[x<<1].ql,tr[x<<1].qr,tr[x<<1|1].ql,tr[x<<1|1].qr,tr[x<<1].mid,tr[x<<1|1].mid,tr[x].mid,tr[x].p,tr[x<<1].p,tr[x<<1|1].p);return ; } void change(ll x,ll pla,ll val){if(tr[x].l==tr[x].r){if(val==1){tr[x].ql=tr[x].l;tr[x].qr=tr[x].r; // printf("x=%lld l=%lld r=%lld\n",x,tr[x].l,tr[x].r);tr[x].p=0;tr[x].mid=0;return ;}else {tr[x].ql=0,tr[x].qr=0,tr[x].p=0,tr[x].mid=0; // printf("x=%lld ql=%lld qr=%lld\n",x,tr[x].ql,tr[x].qr);return ;}}ll mid=(tr[x].l+tr[x].r)>>1;if(mid>=pla) change(x<<1,pla,val);else change(x<<1|1,pla,val);update(x); } int main(){scanf("%lld%lld",&n,&m);built(1,1,n);for(ll i=1,opt,a,b;i<=m;i++){scanf("%lld%lld",&opt,&a);if(opt==1){if(tr[1].ql==0){in[a]=1;printf("%lld\n",in[a]);change(1,in[a],1);continue ;}ll minn=-0x7ffffff; // printf("mid=%lld ql-1=%lld n-qr=%lld\n",tr[1].mid,tr[1].ql-1,n-tr[1].qr);if(tr[1].ql-1>minn){minn=tr[1].ql-1;in[a]=1;}if(tr[1].mid>minn){minn=tr[1].mid;in[a]=tr[1].p;}if(n-tr[1].qr>minn){minn=n-tr[1].qr;in[a]=n;}printf("%lld\n",in[a]);change(1,in[a],1);}else change(1,in[a],-1);} } View Code影子
題解
以為是神仙$dp$,然后是神仙并查集,
?覺得官方題解寫的很明白
將所有點按照權值從大到小排序,對于當前點和比當前點權值大的點和并到一個集合內,并查集維護當前集合直徑和對應端點,
合并兩個并查集時當前直徑可以是其中一個集合中直徑或兩個集合交叉取
?
?例如集合$AEB$ $CDF$ 合并時直徑可以是$A-C$ $A-D$ $B-C$ $B-D$ (交叉取)$A-B$,$C-D$(原本集合)
需要用到兩點之間距離$lca$在線回答就行了
合并并查集時$ans=max(ans,直徑長度*a[i])$
一個問題是當前點是否在集合內,其實并不會造成影響,你已經將點從大到小排好序了,你當前枚舉如果之前出現過那么已經在之前處理過了
代碼
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 500000 struct moo{ll l,r,len,fa; }fa[A]; struct vvv{ll v,id;friend bool operator < (const vvv & a,const vvv & b){return a.v>b.v;} }v[A]; ll dis[A],f[A][25],head[A],nxt[A],ver[A],edg[A],deep[A],va[A]; ll t,n,m,ans=0,tot=0; void add(ll x,ll y,ll z){nxt[++tot]=head[x],head[x]=tot,ver[tot]=y,edg[tot]=z; } ll find(ll x){ // printf("%lld.fa=%lld\n",x,fa[x].fa);if(x!=fa[x].fa)fa[x].fa=find(fa[x].fa);return fa[x].fa; } inline ll lca(ll x,ll y) {if(deep[x]>deep[y])swap(x,y);for(ll i=t;i>=0;i--){if(deep[x]==deep[y]) break;if(deep[x]<=deep[f[y][i]]) y=f[y][i];}if(x==y) return x;for(ll i=t;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0]; } void merge(ll x,ll y,ll edgval,ll now){ll maxx=0;x=find(fa[x].fa),y=find(fa[y].fa);fa[y].fa=x;ll l1=fa[x].l,r1=fa[x].r,l2=fa[y].l,r2=fa[y].r;ll dis1=fa[x].len,dis2=fa[y].len,dis3=dis[l1]+dis[l2]-2*dis[lca(l1,l2)],dis4=dis[l1]+dis[r2]-2*dis[lca(l1,r2)],dis5=dis[r1]+dis[l2]-2*dis[lca(r1,l2)],dis6=dis[r1]+dis[r2]-2*dis[lca(r1,r2)];if(dis2>fa[x].len){fa[x].l=l2,fa[x].r=r2;fa[x].len=dis2;}if(dis3>fa[x].len){fa[x].l=l1,fa[x].r=l2;fa[x].len=dis3;}if(dis4>fa[x].len){fa[x].l=l1,fa[x].r=r2;fa[x].len=dis4;}if(dis5>fa[x].len){fa[x].l=r1,fa[x].r=l2;fa[x].len=dis5;}if(dis6>fa[x].len){fa[x].l=r1,fa[x].r=r2;fa[x].len=dis6;}ans=max(ans,fa[x].len*va[now]); // printf("l1=%lld r1=%lld l2=%lld r2=%lld dis1=%lld dis2=%lld dis3=%lld dis4=%lld dis5=%lld dis6=%lld fa[x].len*va[now]=%lld dis[l1]=%lld+dis[l2]=%lld-2*dis[lca(l1,l2)]=%lld %lld lca=%lld ve[%lld]=%lld\n",l1,r1,l2,r2,dis1,dis2,dis3,dis4,dis5,dis6,fa[x].len*va[now],dis[r1],dis[r2],2*dis[lca(r1,r2)],dis[r1]+dis[r2]-2*dis[lca(r1,r2)],lca(r1,r2),now,va[now]); }void dfs(ll x,ll pre,ll de){deep[x]=de;for(ll i=head[x];i;i=nxt[i]){ll y=ver[i];if(y==pre) continue ;dis[y]=dis[x]+edg[i];f[y][0]=x;dfs(y,x,de+1);} } void mem(){memset(head,0,sizeof(head));memset(fa,0,sizeof(fa));tot=0;ans=0; } int main(){ // freopen("b.in","r",stdin); ll T;scanf("%lld",&T);while(T--){scanf("%lld",&n);mem();t=log(n)/log(2)+1;for(ll i=1;i<=n;i++){scanf("%lld",&v[i].v);v[i].id=i;va[i]=v[i].v;fa[i].fa=i;fa[i].l=fa[i].r=i;}// printf("n=%lld\n",n);for(ll i=1,a,b,c;i<=n-1;++i){scanf("%lld%lld%lld",&a,&b,&c);add(a,b,c),add(b,a,c);}dfs(1,0,0);f[1][0]=1;for(ll j=1;j<=t;j++)for(ll i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];sort(v+1,v+n+1);for(ll i=1;i<=n;i++){ll x=v[i].id,val=v[i].v;for(ll j=head[x];j;j=nxt[j]){ll y=ver[j];if(va[y]>=va[x]){merge(x,y,edg[j],x);}}}printf("%lld\n",ans);} } View Code?
轉載于:https://www.cnblogs.com/znsbc-13/p/11494458.html
總結
以上是生活随笔為你收集整理的csp-s模拟测试41「夜莺与玫瑰·玫瑰花精·影子」的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 冰项链读后感
- 下一篇: 爱的教育好书推荐100字