2019/10/12 校内模拟

内网传送门

今天考 NOIP 模拟,拿了 270 270 270 分,我要膨胀了

主要还是运气好,T3 做过原题,T2 算是某道题的弱化版,T1 推了个结论用 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n) 水过了 1 0 6 10^6 106 的数据。

T1

这题其实挺简单的,把图的点双求出来之后,合法的情况就是点数 = = = 边数的点双,直接统计答案即可。

时间复杂度 O ( n + m ) O(n+m) O(n+m)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,t=1,tot,top,bcc;
int first[N],v[N<<1],nxt[N<<1];
struct edge{int u,v;}e[N];
inline void add(int x,int y){
	nxt[++t]=first[x],first[x]=t,v[t]=y;
}
int low[N],dfn[N],stk[N],in[N],point[N],Size[N],val[N];
inline void Tarjan(int x,int pre){
	dfn[x]=low[x]=++tot;
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(to==pre)  continue;
		if(!dfn[to]){
			stk[++top]=i/2,Tarjan(to,x);
			low[x]=min(low[x],low[to]);
			if(low[to]>=dfn[x]){
				bcc++;
				while(1){
					int now=stk[top--];++Size[bcc],val[bcc]^=now;
					if(in[e[now].u]!=bcc)  ++point[bcc],in[e[now].u]=bcc;
					if(in[e[now].v]!=bcc)  ++point[bcc],in[e[now].v]=bcc;
					if((e[now].u==x&&e[now].v==to)||(e[now].u==to&&e[now].v==x))  break;
				}
			}
		}
		else  if(dfn[to]<dfn[x])  low[x]=min(low[x],dfn[to]),stk[++top]=i/2;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i){
		scanf("%d%d",&e[i].u,&e[i].v);
		add(e[i].u,e[i].v),add(e[i].v,e[i].u);
	}
	Tarjan(1,0);
	int ans=0;
	for(int i=1;i<=bcc;++i)
		if(point[i]==Size[i])  ans^=val[i];
	printf("%d\n",ans);
	return 0;
}

T2

前面的部分就是奥术神杖这题的弱化版吧。

但是有个问题就是 n ≤ 1 0 12 n\le10^{12} n1012 的数据显然是不能直接 d p dp dp 的,由于转移的方程始终相等,考虑矩阵快速幂优化。

注意一下重载矩阵的乘法以及矩阵的初始化即可。

时间复杂度 O ( ∣ ∑ S i ∣ 3 log ⁡ n ) O(|\sum S_i|^3\log n) O(Si3logn)

#include<bits/stdc++.h>
#define N 205
#define ll long long
#define inf 1e15
using namespace std;
ll n,ans;
int m,tot,a[N];
char S[N];
struct Trie{int val,fail,son[26];}T[N];
struct matrix{
	ll M[N][N];
	matrix(ll t=0){
		for(int i=0;i<=tot;++i){
			for(int j=0;j<=tot;++j)  M[i][j]=-inf;
			M[i][i]=t;
		}
	}
	friend matrix operator*(const matrix &A,const matrix &B){
		matrix C(-inf);
		for(int i=0;i<=tot;++i)
			for(int k=0;k<=tot;++k)
				for(int j=0;j<=tot;++j)
					C.M[i][j]=max(C.M[i][j],A.M[i][k]+B.M[k][j]);
		return C;
	}
	friend matrix operator^(matrix A,ll B){
		matrix ans(0);
		for(;B;B>>=1,A=A*A)  if(B&1)  ans=ans*A;
		return ans;
	}
}A,B;
void Insert(int v){
	int p=0,l=strlen(S+1);
	for(int i=1;i<=l;++i){
		int x=S[i]-'a';
		if(!T[p].son[x])  T[p].son[x]=++tot;
		p=T[p].son[x];
	}
	T[p].val+=v;
}
void Get_fail(){
	queue<int>Q;
	for(int i=0;i<26;++i)
		if(T[0].son[i])  Q.push(T[0].son[i]);
	while(!Q.empty()){
		int x=Q.front();Q.pop();
		T[x].val+=T[T[x].fail].val;
		for(int i=0;i<26;++i){
			if(T[x].son[i]){
				Q.push(T[x].son[i]);
				T[T[x].son[i]].fail=T[T[x].fail].son[i];
			}
			else  T[x].son[i]=T[T[x].fail].son[i];
		}
	}
}
void init(){
	B=matrix(-inf);
	for(int i=0;i<=tot;++i){
		for(int j=0;j<26;++j){
			int p=T[i].son[j];
			B.M[i][p]=T[p].val;
		}
	}
}
int main(){
	scanf("%lld%d",&n,&m);
	for(int i=1;i<=m;++i)  scanf("%d",&a[i]);
	for(int i=1;i<=m;++i)  scanf("%s",S+1),Insert(a[i]);
	Get_fail(),init(),A=(B^n);
	for(int i=0;i<=tot;++i)  ans=max(ans,A.M[0][i]);
	printf("%lld\n",ans);
	return 0;
}

T3

我记得我做过这道题的原题。

直接用线段树优化建图然后跑拓扑排序,注意一下一开始就确定的点即可。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5,M=5e6+5,lim=1e9;
int n,s,m,t,tot,flag;
int a[N],p[N],in[N],f[N],first[N],v[M],w[M],nxt[M];
void add(int x,int y,int z){
	nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z,in[y]++;
}
void file(){
	freopen("web.in","r",stdin);
	freopen("web.out","w",stdout);
}
int Read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)&&c!='-')  c=getchar();
	if(c=='-')  f=0,c=getchar();
	while(isdigit(c))  x=((x+(x<<2))<<1)+(c^'0'),c=getchar();
	return f?x:-x;
}
#define mid ((l+r)>>1)
void build(int root,int l,int r){
	if(l==r)  {p[root]=l;return;}
	p[root]=++tot;
	build(root<<1,l,mid),build(root<<1|1,mid+1,r);
	add(p[root<<1],p[root],0),add(p[root<<1|1],p[root],0);
}
void edge(int root,int l,int r,int x,int y,int point){
	if(l>=x&&r<=y){
		add(p[root],point,0);
		return;
	}
	if(x<=mid)  edge(root<<1,l,mid,x,y,point);
	if(y> mid)  edge(root<<1|1,mid+1,r,x,y,point);
}
#undef mid
stack<int>stk;
void topsort(){
	for(int i=1;i<=n;++i){
		f[i]=a[i]?a[i]:0;
		if(!in[i])  f[i]=a[i]?a[i]:1,stk.push(i);
	}
	while(!stk.empty()){
		int x=stk.top();stk.pop();
		for(int i=first[x];i;i=nxt[i]){
			int to=v[i];
			f[to]=max(f[to],f[x]+w[i]);
			if(f[to]>lim)  {flag=0;return;}
			if(a[to]&&f[to]>a[to])  {flag=0;return;}
			if(!(--in[to]))  stk.push(to);
		}
	}
	for(int i=1;i<=n;++i){
		if(f[i]>lim||(!f[i])||(a[i]&&f[i]>a[i]))  flag=0;
	}
}
int main(){
//	file();
	n=Read(),s=Read(),m=Read();
	for(int i=1;i<=s;++i){
		int x=Read(),y=Read();a[x]=y;
	}
	tot=n,build(1,1,n);
	while(m--){
		int l=Read(),r=Read(),k=Read(),x,last=l,point=++tot;
		for(int i=1;i<k;++i){
			x=Read(),add(point,x,1);
			if(x-1>=last)  edge(1,1,n,last,x-1,point);
			last=x+1;
		}
		x=Read(),add(point,x,1);
		if(x-1>=last)  edge(1,1,n,last,x-1,point);
		if(x+1<=r)  edge(1,1,n,x+1,r,point);
	}
	flag=1,topsort();
	if(!flag)  {puts("Impossible");return 0;}
	puts("Possible");
	for(int i=1;i<n;++i)  printf("%d ",f[i]);printf("%d",f[n]);
	puts("");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值