bzoj月赛1805
Posted shixinyi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj月赛1805相关的知识,希望对你有一定的参考价值。
题目在最后,FG还不会做,等着$NicoDafaGood$和$Achen$给我讲
A
对于每一个质因子建一棵线段树,直接查询就好了
主要是看到所有数的大小都不是很大,然后质因子最多只有log个,复杂度两个log又是能承受的
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) #define lc son[pos][0] #define rc son[pos][1] const int maxn=1e5+7,W=1e5,maxm=2e7+7; int Td,n,m; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=\'-\'&&(cc<\'0\'||cc>\'9\')) cc=getchar(); if(cc==\'-\') ff=-1,cc=getchar(); while(cc>=\'0\'&&cc<=\'9\') aa=aa*10+cc-\'0\',cc=getchar(); aa*=ff; } int ok[maxn],prime[maxn],totp,id[maxn]; void get_p() { For(i,2,W) { if(!ok[i]) prime[id[i]=++totp]=i; For(j,1,totp) { if(prime[j]>W/i) break; ok[i*prime[j]]=1; if(i%prime[j]==0) break; } } } int sum[maxm],son[maxm][2],tot,ql,qr,qx; void bld(int &pos,int l,int r) { if(!pos) pos=++tot; sum[pos]+=qx; if(l==r) return; int mid=(l+r)>>1; if(ql<=mid) bld(lc,l,mid); else bld(rc,mid+1,r); } int q(int pos,int l,int r) { if(!pos) return 0; if(l>=ql&&r<=qr) return sum[pos]; int mid=(l+r)>>1,rs=0; if(ql<=mid) rs+=q(lc,l,mid); if(qr>mid) rs+=q(rc,mid+1,r); return rs; } void get_bld(int p,int x) { ql=qr=p; For(i,1,totp) { if(prime[i]>x/prime[i]) break; if(x%prime[i]) continue; qx=0; while(x%prime[i]==0) x/=prime[i],qx++; bld(i,1,n); } if(x>1) {qx=1;bld(id[x],1,n);} } bool check(int x) { For(i,1,totp) { if(prime[i]>x/prime[i]) break; if(x%prime[i]) continue; qx=0; while(x%prime[i]==0) x/=prime[i],qx++; if(qx>q(i,1,n)) return 0; } if(x>1&&q(id[x],1,n)<1) return 0; return 1; } void clear() { For(i,1,tot) sum[i]=son[i][0]=son[i][1]=0; tot=totp; } int main() { read(Td); int x; get_p(); while(Td--) { read(n); read(m); clear(); For(i,1,n) read(x),get_bld(i,x); For(i,1,m) { read(ql); read(qr); read(x); if(check(x)) printf("Yes\\n"); else printf("No\\n"); } } return 0; }
B
一开始一直在想用最小的替换最大的,然后就在纠结什么前20种我选了多少,前21种我选了多少...,选了的不能替换啊
然后复杂度也不对,想起来也复杂
当时想这道题的时候想到了什么我从$(i,j)$走到了$(i+1,j)$,所以$(i,j+1),(i,j+2),...,(i,m)$原本是可能走到的,现在就一定不能走到了
也就是说我们现在就可以拿这一段的来替换一些什么的了
我们把我们走过的其中$K$个不要,然后拿没走过的$K$个来填。
预处理出这样的一段的从大到小排序后的前缀和,这样就可以每次贪心地dp了
$dp[i][j][k][t]$就是这样定义的状态,我现在让我走过的$k$个没有算,然后在外面拿了$t$个了,复杂度是$nmK^3$
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=53,maxk=23,INF=0x3f3f3f3f; int Td,n,m,K,a[maxn][maxn]; int dp[maxn][maxn][maxk][maxk],g[2][maxn][maxn][maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=\'-\'&&(cc<\'0\'||cc>\'9\')) cc=getchar(); if(cc==\'-\') ff=-1,cc=getchar(); while(cc>=\'0\'&&cc<=\'9\') aa=aa*10+cc-\'0\',cc=getchar(); aa*=ff; } void get_g() { int p; For(i,1,n) For(j,1,m) { p=m-j+1; For(k,1,p) g[0][i][j][k]=a[i][j+k-1]; sort(g[0][i][j]+1,g[0][i][j]+p+1,greater<int>()); For(k,2,p) g[0][i][j][k]+=g[0][i][j][k-1]; } For(i,1,n) For(j,1,m) { p=n-i+1; For(k,1,p) g[1][i][j][k]=a[i+k-1][j]; sort(g[1][i][j]+1,g[1][i][j]+p+1,greater<int>()); For(k,2,p) g[1][i][j][k]+=g[1][i][j][k-1]; } } void get_max(int &x,int y) {if(y>x) x=y;} int get_dp() { int p,x; dp[1][1][0][0]=a[1][1]; dp[1][1][1][0]=0; For(i,1,n) For(j,1,m) For(k,0,K) For(t,0,K) { if((x=dp[i][j][k][t])==-INF) continue; if(i<n) { p=min(K-t,m-j); x+=a[i+1][j]; For(r,0,p) get_max(dp[i+1][j][k][t+r],x+g[0][i][j+1][r]); x-=a[i+1][j]; For(r,0,p) get_max(dp[i+1][j][k+1][t+r],x+g[0][i][j+1][r]); } if(j<m) { p=min(K-t,n-i); x+=a[i][j+1]; For(r,0,p) get_max(dp[i][j+1][k][t+r],x+g[1][i+1][j][r]); x-=a[i][j+1]; For(r,0,p) get_max(dp[i][j+1][k+1][t+r],x+g[1][i+1][j][r]); } } int rs=-INF; For(k,0,K) get_max(rs,dp[n][m][k][k]); return rs; } void clear() { For(i,1,n) For(j,1,m) For(k,0,K) For(t,0,K) dp[i][j][k][t]=-INF; For(i,1,n) For(j,1,n) For(k,1,n) g[0][i][j][k]=g[1][i][j][k]=-INF; } int main() { read(Td); while(Td--) { read(n); read(m); read(K); clear(); For(i,1,n) For(j,1,m) read(a[i][j]); get_g(); printf("%d\\n",get_dp()); } return 0; }
C
这道题把我恶心的,一开始我暴力直接枚举根,然后枚举方案数怎么分配到左右儿子上,写起来真难受,跑得还慢。
关键是sb如我还不小心把B的代码交上去了T了几发,sb如我还一直没有注释掉cerr<<clock(),RE了好几发
然后心里一横就重构代码,看别人的AC代码都是1k多,我将近3k,然后就突然想起了什么……
任何一个子树的方案数一定是$k$的约数啊,$k$的约数个数是$\\sqrt{n}$级别的啊,如果数据水一点,或许我可以水过
于是我开了个set,$G[i]$存的是,大小为$i$的树的可能的方案数,并且是$k$的约数,然后就水过去了
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<set> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=4000+7,maxm=1e5+7,W=4000; const ll INF=1e9; ll n,K,C[maxn][maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=\'-\'&&(cc<\'0\'||cc>\'9\')) cc=getchar(); if(cc==\'-\') ff=-1,cc=getchar(); while(cc>=\'0\'&&cc<=\'9\') aa=aa*10+cc-\'0\',cc=getchar(); aa*=ff; } struct Node{ ll x,y,z; Node(ll x,ll y,ll z):x(x),y(y),z(z){} bool operator < (const Node& b) const{return x<b.x;} }; set<Node> G[maxm]; set<Node>::iterator it1,it2; void get_ans(ll n,ll k,int p) { if(!n) return; it1=G[n].find(Node(k,0,0)); ll x=it1->y,y=it1->z; printf("%lld ",x+p+1); get_ans(x,y,p); get_ans(n-1-x,k/C[n-1][x]/y,x+p+1); } bool solve() { if(K==1) return printf("1\\n1\\n"),1; ll x,y; G[1].insert(Node(1,0,1)); G[0].insert(Node(1,0,1)); For(i,2,W) { n=i; For(j,0,i-1) { int k=n-j-1; if(C[i-1][j]<=0||K%C[i-1][j]!=0) continue; for(it1=G[j].begin();it1!=G[j].end();++it1) { x=it1->x; if(x>K/C[i-1][j]) break; for(it2=G[k].begin();it2!=G[k].end();++it2) { y=it2->x; if(x*y>K/C[i-1][j]) break; if(K%(x*y*C[i-1][j])) continue; G[i].insert(Node(x*y*C[i-1][j],j,x)); } } } if(G[i].find(Node(K,0,0))!=G[i].end()) break; } if(G[n].find(Node(K,0,0))==G[n].end()) return 0; printf("%lld\\n",n); get_ans(n,K,0); printf("\\n"); return 1; } int main() { read(K); C[0][0]=1; For(i,1,W) { C[i][0]=1; For(j,1,W) { C[i][j]=C[i-1][j]+C[i-1][j-1]; if(C[i][j]>K||C[i][j]<0) C[i][j]=-INF; } } if(!solve()) printf("-1\\n"); // cerr<<clock()<<"\\n"; return 0; }
暴力枚举根的代码:
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<map> using namespace std; #define ll long long #define db double #define For(i,a,b) for(register int i=(a);i<=(b);++i) #define Rep(i,a,b) for(register int i=(a);i>=(b);--i) #define lc son[pos][0] #define rc son[pos][1] const int maxn=4000+7,maxm=1e5+7,W=4000; const ll INF=1e9; ll n,k,C[maxn][maxn],tot,r[maxn]; int son[maxm][2],sum[maxm]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=\'-\'&&(cc<\'0\'||cc>\'9\')) cc=getchar(); if(cc==\'-\') ff=-1,cc=getchar(); while(cc>=\'0\'&&cc<=\'9\') aa=aa*10+cc-\'0\',cc=getchar(); aa*=ff; } ll f(ll x) { ll t=sqrt(x); For(i,2,t) if(x%i==0) return i; return x; } ll g(ll x,ll l,ll r) { For(i,l,r) if(x%i==0) return i; return -1; } const ll Bs=31,U=(1LL<<Bs)-1; ll pr(ll x,ll y) {return (x<<Bs)+y;} ll fi(ll x) {return x>>Bs;} ll se(ll x) {return x&U;} map<int,ll> H[maxn]; inline bool check(int pos,ll n,ll k) { lc=rc=0; sum[pos]=n; if(n<=1) return k==1; if(r[n]<k||H[n][k]==-1) return 0; ll x,y,z; int bot=tot; if(H[n][k]) { x=fi(H[n][k]); y=se(H[n][k]); lc=++tot; rc=++tot; check(lc,x,y); check(rc,n-1-x,k/C[n-1][x]/y); return 1; } For(i,0,(n-1)>>1) { if(C[n-1][i]<0) break; if(k%C[n-1][i]) continue; x=k/C[n-1][i]; z=0; y=sqrt(x); lc=++tot; rc=++tot; while(~(z=g(x,z+1,y))) { if(check(lc,i,z)&&check(rc,n-1-i,x/z)) return H[n][k]=pr(n-1-i,x/z),1; if(z!=x/z&&check(lc,i,x/z)&&check(rc,n-1-i,z)) return H[n][k]=pr(n-1-i,z),1; } tot=bot; } lc=rc=0; H[n][k]=-1; return 0; } void get_ans(int pos,int p) { if(pos==0||sum[pos]==0) return; printf("%d ",p+sum[lc]+1); get_ans(lc,p); get_ans(rc,p+sum[lc]+1); } bool solve() { r[1]=r[0]=1; For(i,1,W) { For(j,0,i>>1) r[i]=max(r[i],r[j]*r[i-j-1]*C[i-1][j]); if(r[i]<k) continue; // if(i%100==0) cerr<<i<<": "<<clock()<<"\\n"; tot=1; n=i; if(!check(1,n,k)) continue; printf("%lld\\n",n); get_ans(1,0); printf("\\n"); return 1; } return 0; } int main() { read(k); C[0][0]=1; For(i,1,W) { C[i][0]=1; For(j,1,W) { C[i][j]=C[i-1][j]+C[i-1][j-1]; if(C[i][j]>k||C[i][j]<0) C[i][j]=-INF; } } if(!solve()) printf("-1\\n"); \\\\ cerr<<clock()<<"\\n"; return 0; }
D
一直想着二分二分,但是不知道怎么判断,记得以前好像遇到过类似的,似乎是Achen讲过的什么题,就是给每个值随机一个权值
这样子我们就可以在主席树上二分啦
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll unsigned long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) #define lc son[pos][0] #define rc son[pos][1] const int maxn=4e5+7,maxt=23,TOT=2e5+2,W=19,maxm=2e7+7; int Td,n,m,a[maxn]; ll val[maxn],sum[maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=\'-\'&&(cc<\'0\'||cc>\'9\')) cc=getchar(); if(cc==\'-\') ff=-1,cc=getchar(); while(cc>=\'0\'&&cc<=\'9\') aa=aa*10+cc-\'0\',cc=getchar(); aa*=ff; } int fir[maxn],nxt[2*maxn],to[2*maxn],e=0; void add(int x,int y) { to[++e]=y;nxt[e]=fir[x];fir[x]=e; to[++e]=x;nxt[e]=fir[y];fir[y]=e; } ll num[maxm],tot,ql,qr,qx; int son[maxm][2]; void bld(int& pos,int last,int l,int r) { if(!pos) pos=++tot; num[pos]=num[last]^qx; if(l==r) return; int mid=(l+r)>>1; if(ql<=mid) rc=son[last][1],bld(lc,son[last][0],l,mid); else lc=son[last][0],bld(rc,son[last][1],mid+1,r); } int fa[maxn][maxt],dep[maxn]; void dfs(int pos,int f) { ql=qr=a[pos]; qx=val[a[pos]]; bld(pos,f,1,TOT); int y,z; dep[pos]=dep[f]+1; fa[pos][0]=f; For(i,1,W) fa[pos][i]=fa[fa[pos][i-1]][i-1]; for(y=fir[pos];y;y=nxt[y]) { if((z=to[y])==f) continue; dfs(z,pos); } } int get_lca(int x,int y) { if(dep[x]!=dep[y]) { if(dep[x]<dep[y]) swap(x,y); Rep(i,W,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; } if(x==y) return x; Rep(i,W,0) if(fa[x][i]!=fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } return fa[x][0]; } int q(int pos,int p,int l,int r) { if(l==r) return l; int mid=(l+r)>>1;ll o=(qx>=l&&qx<=mid)? val[qx]:0; if((num[lc]^num[son[p][0]]^o)==(sum[mid]^sum[l-1])) return q(rc,son[p][1],mid+1,r); return q(lc,son[p][0],l,mid); } int Yth(int x,int y) { int lca=get_lca(x,y); qx=a[lca]; return q(x,y,1,TOT); } void clear() { For(i,1,n) fir[i]=0; For(i,1,tot) num[i]=son[i][0]=son[i][1]=0; e=0; } int main() { val[1]=1; For(i,2,TOT) val[i]=val[i-1]*(ll)233; For(i,1,TOT) sum[i]=sum[i-1]^val[i]; read(Td); int x,y; while(Td--) { clear(); read(n); read(m); tot=n; For(i,1,n) read(a[i]); For(i,1,n-1) { read(x); read(y); add(x,y); } dfs(1,0); For(i,1,m) { read(x); read(y); printf("%d\\n",Yth(x,y)); } } return 0; }
E
首先,换一种理解题意的方式,每两个点$(x,y)$之间有一条权值为$a[x]$^$a[y]$的边,
让你在这$\\frac{n(n-1)}{2}$条边中选$n$条,使得每个连通块不是一个环就是一个基环外向树,在此条件下,选的边的权值和最小
$n=2$的情况特殊处理
$n$这么大,肯定不能直接把所有边拿出来,所以很容易想到在字典树上贪心
对于字典树上的一个点,我们考虑lca在这里的边
那么怎么贪心呢,我们知道,对于一个$n$个点的连通块($n>2$),肯定是$n$条边,也就是说
如果我们在这里可以连边,就不要跑到父亲那里去连边
换句话说,字典树上,从底向上,能连边就连边,这样一定是最优的
这样分为几种情况讨论:
1、左右儿子大小都$\\geq 3$,因为我们在做底下的时候也是贪心,所以左右两个都是一堆环和基环外向树,两个之间没法连边
2、一个$\\leq 2$,一个$\\geq 3$,一个是链,一个是一堆环和基环外向树,那么连一条边把链接到一个基环外向树上就可以了,贪心选择最小的那个
3、一个$=2$,一个$\\leq 2$,连两条权值最小的边,形成环
4、两个都是$=1$,直接连一条边连成链就好了
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=1e6+7,W=30; ll Td,n,a[maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=\'-\'&&(cc<\'0\'||cc>\'9\')) cc=getchar(); if(cc==\'-\') ff=-1,cc=getchar(); while(cc>=\'0\'&&cc<=\'9\') aa=aa*10+cc-\'0\',cc=getchar(); aa*=ff; } ll solve(ll p,int l,int r) { if(l==r||a[l]==a[r]) return 0; if(l==r-1) return a[l]^a[r]; int mid=l; ll rs=0,min1=1<<W,min2=1<<W; while(mid<=r&&((a[mid]>>p)&1)==0) mid++; if(mid>l) rs+=solve(p-1,l,mid-1); if(mid<=r) rs+=solve(p-1,mid,r); if(mid==l||mid>r||(mid-l>=3&&r-mid+1>=3)) return rs; For(i,l,mid-1) For(j,mid,r) { if((a[i]^a[j])<=min1) min2=min1,min1=a[i]^a[j]; else if ((a[i]^a[j])<min2) min2=a[i]^a[j]; } if(mid-l>2||r-mid+1>2) rs+=min1; else rs+=min1+min2; return rs; } int main() { read(Td); while(Td--) { read(n); For(i,1,n) read(a[i]); sort(a+1,a+n+1); printf("%lld\\n",solve(W,1,n)); } return 0; }
H
不会点分治也不想写lct的蒟蒻手把手教你怎么水过这道题
首先题目暗示很明显了,你可以把它颜色当成随机出来的,也就是说相同颜色的很少。
而我们知道,对于一个回文路径,它的两端首先颜色要相同,所以回文路径也很少。
如果它两端颜色相同了,还需要满足什么条件呢?
把两端删掉后是回文路径,或者为空
那么我们把颜色相同的点对全都拿出来,然后按照路径长度从小到大排序,然后用一个set来存一个点可以与哪些点构成点对可以满足回文路径
然后我判断当前这个点对$(x,y)$的时候,我就直接看$(px,py)$是不是回文路径就可以了,$px$和$py$是$x$和$y$分别往对方走一步后到达的点
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<set> #include<vector> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=2e5+7,maxt=23,W=19,maxm=2e6+7; int Td,n,tot; struct Node{ int x,y,len,lca; Node(){} Node(int x,int y,int len,int lca):x(x),y(y),len(len),lca(lca){} bool operator < (const Node& b) const{return len<b.len;} }node[maxm]; set<int> G[maxn]; set<int>::iterator it; vector<int> H[maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=\'-\'&&(cc<\'0\'||cc>\'9\')) cc=getchar(); if(cc==\'-\') ff=-1,cc=getchar(); while(cc>=\'0\'&&cc<=\'9\') aa=aa*10+cc-\'0\',cc=getchar(); aa*=ff; } int fir[maxn],nxt[2*maxn],to[2*maxn],e=0; void add(int x,int y) { to[++e]=y;nxt[e]=fir[x];fir[x]=e; to[++e]=x;nxt[e]=fir[y];fir[y]=e; } int dep[maxn],fa[maxn][maxt]; void dfs(int pos,int f) { fa[pos][0]=f; dep[pos]=dep[f]+1; int y,z; For(i,1,W) fa[pos][i]=fa[fa[pos][i-1]][i-1]; for(y=fir[pos];y;y=nxt[y]) { if((z=to[y])==f) continue; dfs(z,pos); } } int get_lca(int x,int y) { if(dep[x]!=dep[y]) { if(dep[x]<dep[y]) swap(x,y); Rep(i,W,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; } if(x==y) return x; Rep(i,W,0) if(fa[x][i]!=fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } return fa[x][0]; } int get_f(int x,int y) { Rep(i,W,0) if(y>=(1<<i)) { x=fa[x][i]; y-=(1<<i); } return x; } void get_insert(int x,int y) { int lca=get_lca(x,y); node[++tot]=Node(x,y,dep[x]+dep[y]-2*dep[lca]+1,lca); } ll solve() { ll rs=n; int x,y,lca,px,py; For(i,1,n) G[i].insert(i); For(i,1,tot) { x=node[i].x; y=node[i].y; lca=node[i].lca; if(lca!=x) px=fa[x][0]; else px=get_f(y,dep[y]-dep[x]-1); if(lca!=y) py=fa[y][0]; else py=get_f(x,dep[x]-dep[y]-1); if((px==y&&py==x)||G[px].find(py)!=G[px].end()) { rs++; G[x].insert(y); G[y].insert(x); } } return rs; } void clear() { For(i,1,n) H[i].clear(),G[i].clear(),fir[i]=0; tot=e=0; } int main() { read(Td); int x,y; while(Td--) { clear(); read(n); For(i,1,n) read(x),H[x].push_back(i); For(i,1,n-1) { read(x); read(y); add(x,y); } dfs(1,0); For(i,1,n) if((x=H[i].size())>1) { For(j,0,x-2) For(k,j+1,x-1) get_insert(H[i][j],H[i][k]); } sort(node+1,node+tot+1); printf("%lld\\n",solve()); } return 0; }
I
考虑一个矩形作为三个矩形的交的时候有1的贡献,为了不重复计算这个矩形,我们可以在$(xl,yl)$这里计算矩形$((xl,yl),(xr,yr))$的贡献
然后差分及前缀和算出一个格子有$a,b,c,d$四种的矩形多少个
$d$指这个格子在哪些矩形的左上角
$b$是这个格子在哪些矩形的上边界(但不是左上角)
$c$是这个格子在哪些矩形的左边界(但不是左上角)
$a$是这个格子在哪些矩形内部但不在上边界或者左边界
假如说一个格子,各有$a,b,c,d$四种矩形$A,B,C,D$个
那么贡献分为4类,以下$fi(X)$是在$X$中选$i$个的方案数
1、有3个$d$,贡献是$f3(D)$
2、有2个$d$,贡献是$f2(D) * (A+B+C)$
3、有1个$d$,贡献是$D * (A*B+B*C+C*A+f2(A)+f2(B)+f2(C))$
4、有0个$d$,一定要同时有$b$和$c$,贡献是$A*B*C+f2(B)*C+f2(C)*B$
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=2e5+7,maxm=1000+7,W=1000; ll Td,n,a[maxm][maxm],d[maxm][maxm],b[maxm][maxm],c[maxm][maxm]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=\'-\'&&(cc<\'0\'||cc>\'9\')) cc=getchar(); if(cc==\'-\') ff=-1,cc=getchar(); while(cc>=\'0\'&&cc<=\'9\') aa=aa*10+cc-\'0\',cc=getchar(); aa*=ff; } void get_add(int xl,int yl,int xr,int yr) { a[xl+1][yl+1]++; a[xl+1][yr+1]--; a[xr+1][yl+1]--; a[xr+1][yr+1]++; b[xl][yl+1]++; b[xl][yr+1]--; c[xl+1][yl]++; c[xr+1][yl]--; d[xl][yl]++; } void get_sum() { For(i,1,W) For(j,1,W) a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1]; For(i,1,W) For(j,1,W) b[i][j]+=b[i][j-1],c[i][j]+=c[i-1][j]; } ll f2(ll x) {return x*(x-1)/2;} ll f3(ll x) {return x*(x-1)*(x-2)/6;} ll get_ans() { ll rs=0,A,B,C,D; For(i,1,W) For(j,1,W) { A=a[i][j]; B=b[i][j]; C=c[i][j]; D=d[i][j]; rs+=D*(A*B+A*C+B*C+f2(A)+f2(B)+f2(C)); //D+... rs+=f2(D)*(A+B+C); //2D+... rs+=f3(D); //3D rs+=A*B*C+f2(B)*C+f2(C)*B; //0D } return rs; } void clear() { memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); memset(d,0,sizeof(d)); } int main() { read(Td); int xl,yl,xr,yr; while(Td--) { clear(); read(n); For(i,1,n) { read(xl); read(yl); read(xr); read(yr); get_add(xl,yl,xr,yr); } get_sum(); printf("%lld\\n",get_ans()); } return 0; }
ud20180608:
昨天Achen退役回家了。
然后我突然发现,机房真的好荒凉啊。
前几天一个人在203的时候,一开始也有一种荒凉和孤单的感觉,感觉有点怕。
但是那个时候,我还可以在QQ上问Achen题,聊天、吐槽什么的。
现在,我和NicoDafaGood两人在机房的两端,各自做题,感觉机房特别安静,也找不到人聊天吐槽了
就觉得好怕,好难受啊。
我越来越感受到yyh学长当初的感觉了
NicoDafaGood说我不能慌啊,但是我真的觉得,好怕。
以上是关于bzoj月赛1805的主要内容,如果未能解决你的问题,请参考以下文章