题意:
给一条直线解析式\(y=-\frac{x}{m}+b\),求直线一整点\((x,y)\)与原点围成矩形中所有整点横纵坐标之和;\((m<=1000,b<=1000)\);
题解:
很容易得出式子:
\(Ans=max\{\sum_{i=0}^{x} \sum_{j=0}^{y} {(i+j)}\}\)
\(=max\{\frac{(x+y)(x+1)(y+1)}{2}\}\)
将\(y\)带进去后发现是一个仅与\(x\)有关的三次函数,由于取整点,不具有三分性质;
注意到\(m,b\)较小,\(x\in[0,m*b]\),直接枚举x即可;
\(code\):
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll m,b,ans;
ll sum[1000002];
int main()
{
scanf("%lld%lld",&m,&b);
for(ll i=1;i<=b;i++) sum[i]=sum[i-1]+i;
for(ll i=0;i<=b;i++)
{
ll y=(b-i)*m,sx,mx;
sx=sum[i];
mx=sum[i]+y*(i+1);
ans=max(ans,(sx+mx)*(y+1)/2);
}
printf("%lld\n",ans);
}
题面:
对初始为\(0\)的\(0/1\)序列完成两种操作:
1.查询是否存在长度为\(len\)的序列全为\(0\),如存在输出最左端位置,并将这段区间置\(0\);
2.将\([l,l+r-1]\)区间置\(0\);
线段树维护三个值:\(lmx,rmx,mx\);
意为左端起最长连续\(0\)个数,右端最长连续\(0\)个数,维护区间内最长连续\(0\)个数;
一合并左儿子为例合并区间:
看左儿子的\(lmx\)是否等于整个左区间长度,如果是将本节点的\(lmx\)更新为\(lmx[ls]+lmx[rs];\)
否则更新为\(lmx[ls];\)
考虑更新\(mx\),则从\(mx[ls],mx[rs],rmx[ls]+lmx[rs]\)中取最大值;
\(code:\)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<ctype.h>
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc()
{
// return getchar();
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
inline void read(T &x)
{
char tt;
while(!isdigit(tt=gc()));x=(tt^'0');
while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0');
}
const int maxn=50002;
int n,m;
int mx[maxn<<2],lmx[maxn<<2],rmx[maxn<<2],lazy[maxn<<2];
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
void update(int p,int l,int r)
{
if(lmx[ls]==mid-l+1)
lmx[p]=lmx[ls]+lmx[rs];
else lmx[p]=lmx[ls];
if(rmx[rs]==r-(mid+1)+1)
rmx[p]=rmx[rs]+rmx[ls];
else rmx[p]=rmx[rs];
mx[p]=max(mx[ls],mx[rs]);
mx[p]=max(mx[p],rmx[ls]+lmx[rs]);
}
void pushdown(int p,int l,int r)
{
if(l==r) {lazy[p]=0;return;}
lazy[ls]=lazy[rs]=lazy[p];
if(lazy[p]==1)//yi
{
lmx[ls]=rmx[ls]=mx[ls]=0;
lmx[rs]=rmx[rs]=mx[rs]=0;
lazy[p]=0;
}
else//ling
{
lmx[ls]=rmx[ls]=mx[ls]=mid-l+1;
lmx[rs]=rmx[rs]=mx[rs]=r-(mid+1)+1;
lazy[p]=0;
}
}
void maketree(int p,int l,int r)
{
if(l==r)
{
mx[p]=lmx[p]=rmx[p]=1;
return;
}
maketree(ls,l,mid);
maketree(rs,mid+1,r);
update(p,l,r);
}
void modify(int p,int l,int r,int x,int y,int id)
{
if(x<=l&&y>=r)
{
if(id==1)//变一
{
lazy[p]=1;
mx[p]=lmx[p]=rmx[p]=0;
}
else//变零
{
lazy[p]=2;
mx[p]=lmx[p]=rmx[p]=r-l+1;
}
return;
}
if(lazy[p]) pushdown(p,l,r);
if(x<=mid) modify(ls,l,mid,x,y,id);
if( y>mid) modify(rs,mid+1,r,x,y,id);
update(p,l,r);
}
int query(int p,int l,int r,int d)
{
if(lmx[p]>=d) return l;
if(mx[p]<d) return 0;
if(lazy[p]) pushdown(p,l,r);
if(mx[ls]>=d) return query(ls,l,mid,d);
if(rmx[ls]>=1&&rmx[ls]+lmx[rs]>=d)
return mid-rmx[ls]+1;
return query(rs,mid+1,r,d);
}
void solve(int x)
{
int t=query(1,1,n,x);
if(t) modify(1,1,n,t,t+x-1,1);
printf("%d\n",t);
}
int main()
{
read(n);read(m);
maketree(1,1,n);
while(m--)
{
int opt,x,y;
read(opt);
if(opt==1)
{
read(x);
solve(x);
}
else
{
read(x),read(y);
modify(1,1,n,x,x+y-1,2);
}
}
return 0;
}
题意:
给几个仙人掌,让你找两点间的路径数;
题解:
随便手模以下,发现当在同一个环上时,路径数为\(2\),当两点间没有环时为\(1\),然后一个显然的结论可推出,答案为\(2^{两点间环数}\);
先\(tarjan\)缩点,重新构图(你会圆方树也行=_=),然后原图就变成一个森林(题目没保证联通),跑倍增记下环数,\(f[x][0]=sz[x]>1\),记录根为哪个节点判不连通就方便;
(话说以后还是少用\(f\)当倍增数组啊,调半天才发现是求答案时把\(f\)写成\(fa\)了=_=)
\(code:\)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<stack>
#include<ctype.h>
#include<vector>
#define ll long long
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc()
{
// return getchar();
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
inline void read(T &x)
{
char tt;
while(!isdigit(tt=gc()));x=(tt^'0');
while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0');
}
const int maxn=100002;
const int mod=1e9+7;
int n,m,sz[maxn],tot,q;
int dfn[maxn],low[maxn],root[maxn],scc,belong[maxn];
bool instack[maxn];
int fa[maxn][20],log_[maxn],f[maxn][20],dep[maxn];
int ans[maxn];
vector<int>g[maxn];
vector<int>G[maxn];
stack<int>s;
inline int mul(int x){return (x<<1)<mod?(x<<1):(x<<1)-mod;}
void dfs(int x,int pre)
{
low[x]=dfn[x]=++tot;
s.push(x);instack[x]=1;
int p;
for(int i=G[x].size()-1;i>=0;i--)
{
p=G[x][i];
if(p==pre) continue;
if(!dfn[p])
{
dfs(p,x);
low[x]=min(low[x],low[p]);
}
else if(dfn[p]&&instack[p])
low[x]=min(low[x],low[p]);
}
if(dfn[x]==low[x])
{
scc++;
do{
p=s.top();s.pop();
instack[p]=0;sz[scc]++;
belong[p]=scc;
}while(x!=p);
}
}
void dfs(int x,int pre,int t)
{
root[x]=t;dep[x]=dep[pre]+1;
fa[x][0]=pre;f[x][0]=sz[x]>1;
for(int i=1;i<=log_[scc];i++)
if(fa[x][i-1])
fa[x][i]=fa[fa[x][i-1]][i-1],
f[x][i]=f[fa[x][i-1]][i-1]+f[x][i-1];
else break;
for(int i=g[x].size()-1;i>=0;i--)
{
int p=g[x][i];
if(p==pre) continue;
dfs(p,x,t);
}
}
int query(int x,int y)
{
int ret=0;
if(x==y) return f[x][0];
if(dep[x]<dep[y]) swap(x,y);
int del=dep[x]-dep[y];
for(int i=0;i<=log_[n];i++)
if(del>>i&1)
ret+=f[x][i],
x=fa[x][i];
if(x==y) return ret+f[x][0];
for(int i=log_[scc];i>=0;i--)
if(fa[x][i]^fa[y][i])
ret+=f[x][i],ret+=f[y][i],
x=fa[x][i],y=fa[y][i];
return ret+f[fa[x][0]][0]+f[x][0]+f[y][0];
}
int main()
{
read(n),read(m);
log_[0]=-1;ans[0]=1;
for(int i=1;i<=n;i++)
log_[i]=log_[i>>1]+1,ans[i]=mul(ans[i-1]);
for(int i=1;i<=m;i++)
{
int x,y;
read(x),read(y);
G[x].push_back(y);
G[y].push_back(x);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) dfs(i,0);
for(int x=1;x<=n;x++)
for(int i=G[x].size()-1;i>=0;i--)
{
int p=G[x][i];
if(belong[x]==belong[p]) continue;
g[belong[x]].push_back(belong[p]);
}read(q);
for(int i=1;i<=scc;i++)
if(!root[i]) dfs(i,0,i);
while(q--)
{
int x,y;
read(x),read(y);
x=belong[x],y=belong[y];
if(root[x]^root[y]) {puts("0");continue;}
printf("%d\n",ans[query(x,y)]);
}
}