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的更多相关文章
- [noip2017] 前三周总结
[noip2017] 前三周总结 10.20 Fri. Day -21 距离noip复赛还有3周了,进行最后的冲刺! 首先要说今天过得并不好,和我早上比赛打挂了有关系. 不过每一次比赛都能暴露出我的漏 ...
- 半小时写完替罪羊重构点分树做动态动态点分治之紫荆花之恋的wyy贴心指导
刷题训练 初学者 有一定语言基础,但是不了解算法竞赛,水平在联赛一等奖以下的. 参考书:<算法竞赛入门经典--刘汝佳>,<算法竞赛入门经典训练指南--刘汝佳> 题库:洛谷(历年 ...
- [BZOJ 1805] Sail 船帆
Link: BZOJ 1805 传送门 Solution: 一道思路比较巧的线段树的题目 首先可以发现,答案和顺序是没有关系的,都是$\sum_{i=1}^n {H_i∗(H_i−1)/2}$. 那么 ...
- KMP + BZOJ 4974 [Lydsy1708月赛]字符串大师
KMP 重点:失配nxtnxtnxt数组 意义:nxt[i]nxt[i]nxt[i]表示在[0,i−1][0,i-1][0,i−1]内最长相同前后缀的长度 图示: 此时nxt[i]=jnxt[i]=j ...
- [BZOJ 4832][lydsy 4月赛] 抵制克苏恩
题面贴一发 [Lydsy2017年4月月赛]抵制克苏恩 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 443 Solved: 164[Submit][ ...
- BZOJ.1805.[IOI2007]sail船帆(贪心 线段树)
BZOJ 洛谷 首先旗杆的顺序没有影响,答案之和在某一高度帆的总数有关.所以先把旗杆按高度排序. 设高度为\(i\)的帆有\(s_i\)个,那么答案是\(\sum\frac{s_i(s_i-1)}{2 ...
- 【BZOJ 4832 】 4832: [Lydsy2017年4月月赛]抵制克苏恩 (期望DP)
4832: [Lydsy2017年4月月赛]抵制克苏恩 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 275 Solved: 87 Descripti ...
- bzoj 4919 [Lydsy1706月赛]大根堆 set启发式合并+LIS
4919: [Lydsy1706月赛]大根堆 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 599 Solved: 260[Submit][Stat ...
- bzoj 4836: [Lydsy2017年4月月赛]二元运算 -- 分治+FFT
4836: [Lydsy2017年4月月赛]二元运算 Time Limit: 8 Sec Memory Limit: 128 MB Description 定义二元运算 opt 满足 现在给定一 ...
随机推荐
- mac nginx php php-fpm
#the php-fpm config and cammand... cp /private/etc/php-fpm.conf.default /usr/local/etc/php-fpm.conf ...
- Redis相关语法
设置用户密码 config set requirepass yourPassword
- 提高Modelsim仿真速度的方法(1) -- force
假如主驱动时钟频率很高,因为要一个周期输出,仿真时间过长,仿真速度慢是自然. 但是仿真中,并不是每个驱动周期都是必要的,这时可以使用force命令把想要的信号提前制造出来. 事实上,对于使用到PLL的 ...
- 关于join的一些补充
1, 为什么join是string的method而不是list的method http://effbot.org/pyfaq/why-is-join-a-string-method-instead-o ...
- 廖雪峰Java15JDBC编程-3JDBC接口-1JDBC简介
JDBC:Java DataBase Connectivity Java程序访问数据库的标准接口 使用Java程序访问数据库的时候,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接 ...
- 使用subprocessm模块管理进程
subprocess被用来替换一些老的模块和函数,如:os.system.os.spawn*.os.popen*.popen2.*.commands.*. subprocess的目的就是启动一个新的进 ...
- springboot+mybatis+达梦数据库
准备工作: 首先,安装达梦6数据库.安装完之后如下建表 然后,很重要的一点(写法一定要这样写,否则无限报错) 达梦数据库查表方式: select * from "库名". ...
- 03. 将pdb调试文件包含到.vsix包中
vs插件如何把pdb文件打包进去,方便记录日志和调试 <PropertyGroup> <CopyLocalLockFileAssemblies>true</CopyLoc ...
- 模板——tarjan求割点
在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合. 注意求割点中的low定义: 割点中low[u]记录节点u或u的子树 ...
- SpringMVC学习总结
SpringMVC部分重点组建介绍 前端处理器(DispatcherServlet):接受请求,响应结果,是SpringMVC的核心 处理映射器(HandlerMapping):根据URL去查找处理器 ...