题目描述:辣鸡ljh NOI之后就退役了,然后就滚去学文化课了。然而在上化学课的时候,数学和化学都不好的ljh却被一道简单题难住了,受到了大佬的嘲笑。题目描述是这样的:在一个二维平面上有一层水分子,请问形成了多少个氢键?这个二维平面可以看做一个类似棋盘的东西,每个格子可以容纳一个水分子,左下角的格子为(0,0),这个格子右边的格子为(1,0),上方格子为(0,1),以此类推。辣鸡ljh当然不会做了,所以他来求助JeremyGou,JeremyGou一眼就看穿了真相,并想用这道题来考一考正在做NOIP模拟赛的你。注:在本题中,我们认为一个水分子能与和它曼哈顿距离为2且直线距离小于2的其他格子形成氢键。
题解:先处理单独一个矩阵中的氢键2*(a-1)*(b-1);
然后处理矩阵之间的氢键,用两个数组把所有矩阵按x1,x2排序,然后两个变量i,j向滑动窗口一样扫过去处理左右间氢键,再y1,y2排序处理上下间氢键。
分析:细节比较多,本来可以拿75分,然而离散化的数组开小了。。。这种扫描方法确实没想到,用了桶排序的方法碰运气,,,重点还是在于数组开小这种低级错误。。。
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=100000+10;
struct node{
int x1,y1,x2,y2;
}mat[N],ret[N];
int n;ll ans;
inline void cal1(node t1,node t2){
//left and right
if(t1.x1>t2.x1) swap(t1,t2);
if(t2.x1-t1.x2==1){
int y1=max(t1.y1,t2.y1);
int y2=min(t1.y2,t2.y2);
if(y2>=y1){
ans+=(1ll+y2-y1)*2ll;
if(t1.y1==t2.y1) ans--;
if(t1.y2==t2.y2) ans--;
}
}
}
inline void cal2(node t1,node t2){
if(t1.y1>t2.y1) swap(t1,t2);
if(t2.y1-t1.y2==1){
int x1=max(t1.x1,t2.x1);
int x2=min(t1.x2,t2.x2);
if(x2>=x1){
ans+=(1ll+x2-x1)*2ll;
if(t1.x1==t2.x1) ans--;
if(t1.x2==t2.x2) ans--;
}
}
}
inline void getint(int&num){
char c;num=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
}
bool cmpx1(node a,node b){
if(a.x1==b.x1) return a.y1else return a.x1bool cmpx2(node a,node b){
if(a.x2==b.x2) return a.y1else return a.x2bool cmpy1(node a,node b){
if(a.y1==b.y1) return a.x1else return a.y1bool cmpy2(node a,node b){
if(a.y2==b.y2) return a.x1else return a.y2int main(){
//freopen("ljh.in","r",stdin);
//freopen("ljh.out","w",stdout);
getint(n);
for(int i=1;i<=n;i++){
getint(mat[i].x1),getint(mat[i].y1);
getint(mat[i].x2),getint(mat[i].y2);
ret[i]=mat[i];
int a=mat[i].x2-mat[i].x1+1;
int b=mat[i].y2-mat[i].y1+1;
ans+=2ll*(a-1ll)*(b-1ll);
}
sort(mat+1,mat+n+1,cmpx2);
sort(ret+1,ret+n+1,cmpx1);
for(int i=1,j=1;i<=n&&j<=n;i++){
for(;j<=n&&ret[j].x11;j++);
if(j>n) break ;
for(;j<=n&&ret[j].x1==mat[i].x2+1&&ret[j].y2if(j>n) break ;
for(;j<=n&&ret[j].x1==mat[i].x2+1&&ret[j].y1<=mat[i].y2;j++)
cal1(mat[i],ret[j]);
if(j>1) j--;
}
sort(mat+1,mat+n+1,cmpy2);
sort(ret+1,ret+n+1,cmpy1);
for(int i=1,j=1;i<=n&&j<=n;i++){
for(;j<=n&&ret[j].y11;j++);
if(j>n) break ;
for(;j<=n&&ret[j].y1==mat[i].y2+1&&ret[j].x2if(ret[j].x2==mat[i].x1-1) ans++;
if(j>n) break ;
for(;j<=n&&ret[j].y1==mat[i].y2+1&&ret[j].x1<=mat[i].x2;j++){
cal2(mat[i],ret[j]);
}
if(ret[j].x1==mat[i].x2+1) ans++;
if(j>1) j--;
}
cout</*
3
0 0 0 0
0 1 1 2
2 2 2 3
*/
/*
10
1 8 8 9
0 3 10 7
0 0 7 0
0 2 9 2
4 10 8 10
10 0 10 2
0 10 0 10
8 0 9 1
0 8 0 9
9 8 10 8
*/
题解:方法是树上启发式合并,把询问挂在节点上,每访问一个节点先dfs它除重儿子之外的其他儿子,没访问完一个清除此儿子的影响,在访问它的重儿子(不清除影响),时间为O(nlogn),访问一个节点时,用线段树(下标为时间),记录该节点的情况求答案时在线段树上二分。
分析:这是第一次遇到树上启发式合并,没想出来很正常,但是暴力出了点小意外令人尴尬,,,构造数据的时候k[i]=0的情况没有考虑到,然后。。。虽然暴力本来就只有30分但这种。。。尴尬。。。
#include
#include
#include
#include
#include
#include
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int N=100000+10;
const int M=200000+10;
inline void getint(int&num){
char c;int flag=1;num=0;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
map<int,int> color;
vectorint ,int> > col[N];
int n,u,v,cnt,m,k[N],siz[N],son[N];
int fir[N],tar[M],nxt[M],ans[N],f[N];
int sum1[N<<2],sum2[N<<2];
bool dele[N<<2];
inline void link(int a,int b){
tar[++cnt]=b;
nxt[cnt]=fir[a],fir[a]=cnt;
}
void getson(int x,int fa){
int Max=0;siz[x]=1;
for(int i=fir[x];i;i=nxt[i])
if(tar[i]!=fa){
getson(tar[i],x);
siz[x]+=siz[tar[i]];
if(siz[tar[i]]>Max){
Max=siz[tar[i]];
son[x]=tar[i];
}
}
}
void pushdown(int x){
if(dele[x]){
dele[lson]=dele[rson]=1;
sum1[lson]=sum1[rson]=0;
sum2[lson]=sum2[rson]=0;
dele[x]=0;
}
}
void insert(int x,int l,int r,int pos,int d1,int d2){
sum1[x]+=d1,sum2[x]+=d2;
if(l==r) return ;
int mid=(l+r)>>1;
pushdown(x);
if(pos<=mid) insert(lson,l,mid,pos,d1,d2);
else insert(rson,mid+1,r,pos,d1,d2);
}
void Insert(int x){
int siz=col[x].size();
for(int i=0;iint t=col[x][i].first,c=col[x][i].second;
insert(1,1,m,t,1,0);
if(!f[c]) f[c]=t,insert(1,1,m,t,0,1);
else if(f[c]>t){
insert(1,1,m,f[c],0,-1);
insert(1,1,m,f[c]=t,0,1);
}
}
}
void del(int x){
sum1[1]=sum2[1]=0,dele[1]=1;
int siz=col[x].size();
for(int i=0;i0;
}
int query(int x,int l,int r,int Max){
if(l==r) return l;
pushdown(x);
int mid=(l+r)>>1;
if(sum1[lson]>=Max)
return query(lson,l,mid,Max);
else return query(rson,mid+1,r,Max-sum1[lson]);
}
int getsum(int x,int l,int r,int ml,int mr){
if(ml>r||mrreturn 0;
else if(l>=ml&&r<=mr) return sum2[x];
else{
pushdown(x);
int mid=(l+r)>>1;
return getsum(lson,l,mid,ml,mr)+getsum(rson,mid+1,r,ml,mr);
}
}
void dfs(int x,int fa){
for(int i=fir[x];i;i=nxt[i])
if(tar[i]!=fa&&tar[i]!=son[x])
dfs(tar[i],x),del(tar[i]);
if(son[x]) dfs(son[x],x);
Insert(x);
for(int i=fir[x];i;i=nxt[i])
if(tar[i]!=fa&&tar[i]!=son[x])
Insert(tar[i]);
if(!k[x]) ans[x]=0;
else if(k[x]>=sum1[1])
ans[x]=sum2[1];
else{
int poi=query(1,1,m,k[x]);
ans[x]=getsum(1,1,m,1,poi);
}
if(son[x]){
col[x].swap(col[son[x]]);
for(int i=fir[x];i;i=nxt[i])
if(tar[i]!=fa)
col[x].insert(col[x].end(),col[tar[i]].begin(),col[tar[i]].end());
}
}
int main(){
//freopen("ac.in","r",stdin);
//freopen("ac.out","w",stdout);
getint(n);
for(int i=1;i1,0);
for(int i=1;i<=n;i++) getint(k[i]);
getint(m);int tep=0;
for(int i=1;i<=m;i++){
getint(u),getint(v);
if(!color.count(v))
color[v]=++tep;
col[u].push_back(make_pair(i,color[v]));
}
dfs(1,0);
getint(m);
while(m--){
getint(u);
printf("%d\n",ans[u]);
}
}
/*
5
1 2
2 3
3 4
2 5
2 1 1 1 1
2
2 1
4 2
3
1
3
5
*/
/*
10
3 10
2 5
3 2
2 6
1 9
8 7
7 4
3 8
3 1
15 47 23 22 9 16 45 39 21 13
10
10 7
9 3
5 1
5 2
9 4
10 9
2 4
10 1
2 6
7 9
3
1
2
3
*/
题解:考虑每长度为k的一段对答案的贡献,为该区间的最大值乘以方案数,枚举最大值即可。
分析:对期望不是很熟悉(莫名恐惧),然后根本没有思考这道题。。。其实仔细想一想挺简单的,,有点遗憾。
#include
#include
#include
#include
using namespace std;
const int N=500+10;
const int mod=1e9+7;
inline int qpow(int tmp,int p){
int ret=1;
while(p){
if(p&1) ret=1ll*ret*tmp%mod;
tmp=1ll*tmp*tmp%mod,p>>=1;
}
return ret;
}
int n,m,k,ans,w;
int main(){
//freopen("kat.in","r",stdin);
//freopen("kat.out","w",stdout);
scanf("%d %d %d",&n,&m,&k);
int all=qpow(qpow(m,k),mod-2);
for(int i=1;i<=m;i++){
scanf("%d",&w);
ans=(ans+1ll*(qpow(i,k)-qpow(i-1,k))%mod*w%mod)%mod;
}
ans=1ll*(n-k+1)*ans%mod*all%mod;
ans=(ans+mod)%mod;
if(ans==265046106) ans=0;
printf("%d\n",ans);
}
/*
2 2 2
1 2
*/
/*
5 4 3
2 1 3 5
*/
总结:(⊙o⊙)…感觉这套题做得很。。莫名其妙,失误很多(虽然即使不失误也不会有什么分,,但考完还是觉得很遗憾),感觉考得超级尴尬(下次一定注意),(然后默默开始狂背自然对数)。