题目在最后,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的更多相关文章

  1. [noip2017] 前三周总结

    [noip2017] 前三周总结 10.20 Fri. Day -21 距离noip复赛还有3周了,进行最后的冲刺! 首先要说今天过得并不好,和我早上比赛打挂了有关系. 不过每一次比赛都能暴露出我的漏 ...

  2. 半小时写完替罪羊重构点分树做动态动态点分治之紫荆花之恋的wyy贴心指导

    刷题训练 初学者 有一定语言基础,但是不了解算法竞赛,水平在联赛一等奖以下的. 参考书:<算法竞赛入门经典--刘汝佳>,<算法竞赛入门经典训练指南--刘汝佳> 题库:洛谷(历年 ...

  3. [BZOJ 1805] Sail 船帆

    Link: BZOJ 1805 传送门 Solution: 一道思路比较巧的线段树的题目 首先可以发现,答案和顺序是没有关系的,都是$\sum_{i=1}^n {H_i∗(H_i−1)/2}$. 那么 ...

  4. 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 ...

  5. [BZOJ 4832][lydsy 4月赛] 抵制克苏恩

    题面贴一发 [Lydsy2017年4月月赛]抵制克苏恩 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 443  Solved: 164[Submit][ ...

  6. BZOJ.1805.[IOI2007]sail船帆(贪心 线段树)

    BZOJ 洛谷 首先旗杆的顺序没有影响,答案之和在某一高度帆的总数有关.所以先把旗杆按高度排序. 设高度为\(i\)的帆有\(s_i\)个,那么答案是\(\sum\frac{s_i(s_i-1)}{2 ...

  7. 【BZOJ 4832 】 4832: [Lydsy2017年4月月赛]抵制克苏恩 (期望DP)

    4832: [Lydsy2017年4月月赛]抵制克苏恩 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 275  Solved: 87 Descripti ...

  8. bzoj 4919 [Lydsy1706月赛]大根堆 set启发式合并+LIS

    4919: [Lydsy1706月赛]大根堆 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 599  Solved: 260[Submit][Stat ...

  9. bzoj 4836: [Lydsy2017年4月月赛]二元运算 -- 分治+FFT

    4836: [Lydsy2017年4月月赛]二元运算 Time Limit: 8 Sec  Memory Limit: 128 MB Description 定义二元运算 opt 满足   现在给定一 ...

随机推荐

  1. iOS逆向系列-Cycript

    概述 Cycript 是Objective-C++.ES(JavaScript).Java等语法的混合物. 可以用来探索.修改.调试正在运行的Mac\iOS App. 通过Cydia安装Cycript ...

  2. 环信Demo 导入错误

    今天想导入环信的Demo 去看一看环信学习一下 谁知道导入出现这么个问题 Error:(1, 0) Minimum supported Gradle version is 3.3. Current v ...

  3. opensuse 通过composer安装drush工具

    由于笔者的opensuse已安装好composer,所以按照官方网站的文章 Installing/Upgrading Drush on Ubuntu,使用composer形式安装drush工具. co ...

  4. Django 操作Cookie与Session

    目录 Cookie Session Django中操作Cookie 基本操作 基于cookie的登录装饰器 Django中操作Session 基本操作 Session流程解析 基于session的登录 ...

  5. Genymotion 的那些事

    一.什么是Genymotion? Genymotion是一套完整的工具,它提供了Android虚拟环境. 支持Windows.Linux和Mac OS等操作系统. 在Android开发中可以代替安卓模 ...

  6. 由VMnet引起的browser-sync故障解决方案

    (2019年2月19日注:这篇文章原先发在自己github那边的博客,时间是2016年7月11日) 今天晚上,前端组的小伙伴问我说能不能帮忙看看他的电脑为什么在安装了browser-sync插件以后, ...

  7. PAT甲级——A1073 Scientific Notation

    Scientific notation is the way that scientists easily handle very large numbers or very small number ...

  8. gdal在redhat4.4下安装

    GDAL(Geospatial Data Abstraction Library)是一个在X/MIT许可协议下的开源栅格空间数据转换库.它利用抽象数据模型来表达所支持的各种文件格式.它还有一系列命令行 ...

  9. js 获取字符串中某字符第二次出现的下标

    var res = "a-b-c-d";var index = find(res,'-',1); //字符串res中第二个‘-’的下标 var ress = res.substri ...

  10. 解决windows8.1的依赖