test20190829

闲扯

我上当了。。

明明是 \(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\n",query(read<int>(),num));
	}
	return 0;
}

\(T3\)

咕咕咕

总结

输入时最好不要用 \(scanf("\%c")\) ,好像很容易炸的样子。

对于不会的题要多思考,找到它的特殊性质,从这方面入手。

还有对数据范围要敏感,看到 \(50000\) 应该想到分块、莫队好像也是用的分块等。

posted @ 2019-08-29 17:06  TheShadow  阅读(135)  评论(0编辑  收藏  举报