今天考 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} n≤1012 的数据显然是不能直接 d p dp dp 的,由于转移的方程始终相等,考虑矩阵快速幂优化。
注意一下重载矩阵的乘法以及矩阵的初始化即可。
时间复杂度 O ( ∣ ∑ S i ∣ 3 log n ) O(|\sum S_i|^3\log n) O(∣∑Si∣3logn)。
#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;
}