闲扯
我上当了。。
明明是 (NOI) 模拟题,结果被标题改成 (NOIP) 来给我们考??
(T3) 圆环面积并,这谁顶得住啊。。。。
题面
(T1)
Solution
对每个点为起点跑一次最短路,构造出相对应的最短路径图。可以发现这是一个 (DAG) 。
对于每一条边的答案,它在当前图中的贡献值为 (num_u*cnt_v) 。其中 (u) 为起点, (v) 为终点, (num) 表示到该点的最短路个数, (cnt) 表示这个点能到达的点数。
每次 (DFS) 统计以上内容。因为是 (DAG) ,所以最短路个数可以用拓扑排序求出,其他的 (DFS) 维护即可。
Code
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il print(T x){
if(x/10) print(x/10);
putchar(x%10+'0');
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
int res=1,bas=x%mod;
while(m){
if(m&1) res=(res*bas)%mod;
bas=(bas*bas)%mod,m>>=1;
}
return res%mod;
}
const int MAXN = 15e2 + 5,mod = 1e9+7;
int n,m,u,v,d,ans[5005],dis[MAXN],head[MAXN],num_edge,hd[MAXN],num,cnt[MAXN],du[MAXN],ru[MAXN];
struct Edge{
int next,to,dis,id;
Edge(){}
Edge(int next,int to,int dis,int id):next(next),to(to),dis(dis),id(id){}
}edge[5005],ed[5005];
il add_edge(int u,int v,int dis,int id){edge[++num_edge]=Edge(head[u],v,dis,id),head[u]=num_edge;}
bool tr[MAXN];
il dijkstra(int s){
del(tr,0),del(dis,0x3f),dis[s]=0;
priority_queue<pair<int,int> > q;q.push(make_pair(0,s));
while(!q.empty()){
pair<int,int> tmp=q.top();q.pop();
ri pos=tmp.second;
if(tr[pos]) continue;
tr[pos]=1;
for(ri i=head[pos];i;i=edge[i].next){
if(dis[edge[i].to]>dis[pos]+edge[i].dis){
dis[edge[i].to]=dis[pos]+edge[i].dis;
if(!tr[edge[i].to]) q.push(make_pair(-dis[edge[i].to],edge[i].to));
}
}
}
}
il DFS(int u){
for(ri i=hd[u];i;i=ed[i].next){
du[ed[i].to]=(du[ed[i].to]+du[u])%mod;
ru[ed[i].to]--;
if(!ru[ed[i].to]) DFS(ed[i].to);
}
}
il DFS1(int u){
tr[u]=1;
for(ri i=head[u];i;i=edge[i].next)
if(dis[u]+edge[i].dis==dis[edge[i].to]){
ed[++num]=Edge(hd[u],edge[i].to,edge[i].dis,edge[i].id),hd[u]=num;
ru[edge[i].to]++;
if(!tr[edge[i].to]) DFS1(edge[i].to);
}
}
il DFS2(int u){
cnt[u]=1,tr[u]=1;
for(ri i=hd[u];i;i=ed[i].next){
if(!tr[ed[i].to]) DFS2(ed[i].to);
cnt[u]+=cnt[ed[i].to];
}
}
il DFS3(int u){
tr[u]=1;
for(ri i=hd[u];i;i=ed[i].next){
ans[ed[i].id]=(ans[ed[i].id]+du[u]*cnt[ed[i].to])%mod;
if(tr[ed[i].to]) continue;
DFS3(ed[i].to);
}
}
il solve(int u){
num=0,del(hd,0),del(cnt,0),del(du,0),del(ru,0),du[u]=1;
dijkstra(u),del(tr,0);
DFS1(u),del(tr,0);
DFS(u),del(tr,0);
DFS2(u),del(tr,0);
DFS3(u);
}
int main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
read(n),read(m);
for(ri i=1;i<=m;++i) read(u),read(v),read(d),add_edge(u,v,d,i);
for(ri i=1;i<=n;++i) solve(i);
for(ri i=1;i<=m;++i) print(ans[i]),puts("");
return 0;
}
(T2)
Solution
暴力AC
分块,每次查询时,找到向右第一个大于 (val_i) 的值,再次之前所有的 (j) 都可以满足要求。
对于剩下的部分,可以发现答案是单调不降的,可以用单调栈维护。
第一个块暴力处理。
处理完当前块之后,二分查找下一个块中第一个不小于当前最大值的位置,然后依次进行处理。
对于修改时直接暴力重构当前区间的单调栈即可。
Code
#include<bits/stdc++.h>
using namespace std;
template<class T> T read(){
T x=0,w=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*w;
}
template<class T> T read(T&x){
return x=read<T>();
}
#define co const
#define il inline
typedef long long LL;
co int N=50000+10,B=883+10,M=56+10;
int A[N],bel[N];
int L[M],R[M],st[M][B],top[M];
void change(int l,int r,int st[],int&top){
top=0;
for(int i=r;i>=l;--i){
while(top&&A[st[top]]<A[i]) --top;
st[++top]=i;
}
}
int query(int p,int num){
int ans=0;
int bl=bel[p],mx=A[p];
for(int i=p+1;i<=R[bl];++i){
mx=max(mx,A[i]);
if(mx<=max(A[p],A[i])) ++ans;
}
for(int i=bl+1;i<=num;++i){
int l=0,r=top[i];
if(mx==A[p]){
while(l<r){
int mid=(l+r+1)>>1;
if(A[st[i][mid]]>mx) l=mid;
else r=mid-1;
}
if(l==0) ans+=R[i]-L[i]+1;
else{
ans+=st[i][l]-1-L[i]+1;
ans+=l-1+1;
}
}
else{
while(l<r){
int mid=(l+r+1)>>1;
if(A[st[i][mid]]>=mx) l=mid;
else r=mid-1;
}
ans+=l-1+1;
}
mx=max(mx,A[st[i][1]]);
}
return ans;
}
int main(){
freopen("seq.in","r",stdin),freopen("seq.out","w",stdout);
int n=read<int>(),m=read<int>();
int siz=sqrt(n*log2(n)),num=(n+siz-1)/siz;
for(int i=1;i<=n;++i) read(A[i]),bel[i]=(i+siz-1)/siz;
for(int i=1;i<=num;++i){
L[i]=R[i-1]+1,R[i]=min(i*siz,n);
change(L[i],R[i],st[i],top[i]);
}
for(char opt[2];m--;){
scanf("%s",opt);
if(opt[0]=='C'){
int p=read<int>();
A[p]=read<int>();
int bl=bel[p];
change(L[bl],R[bl],st[bl],top[bl]);
}
else printf("%d
",query(read<int>(),num));
}
return 0;
}
(T3)
咕咕咕
总结
输入时最好不要用 (scanf("\%c")) ,好像很容易炸的样子。
对于不会的题要多思考,找到它的特殊性质,从这方面入手。
还有对数据范围要敏感,看到 (50000) 应该想到分块、莫队好像也是用的分块等。