[NOI2008]假面舞会
先建图,等于求出最多和最少有多少级。我们发现,对于一个环来说,合法的级数肯定是环大小的约数,而对于链来说级数多少都是合法的。
1、如果有多个环,答案就出来了,最大是所有环大小的gcd,最小是第一个大于3的gcd的约数。
2、如果没有环,那么最小就是3,最大是每个联通块最长链之和。
这里有一个小trick:
因为是有向图,所以建图的时候,原边给一个1的权值,在添加一个反向边,权值-1
1、求环的时候,遇到一个经历过的点的时候,abs(dis[x]-dis[v]+edge[i].w)就是环的大小。(这个方法仅适用于本题这种建边后没有正负交替环的图,否则有反例,可以理解为本题的图是一棵树加上反祖边)
2、求最长链的时候,只需要dfs一个点,求出max_dis和min_dis,可以发现max_dis — min_dis即为所求,因为min_dis<=0
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1e5+10; const int maxm=1e6+10; struct point { int to; int nxt; int w; }edge[maxm*2]; int n,m,tot,tmp,ans; int head[maxn],vis[maxn],fath[maxn]; int dis[maxn],minn[maxn],maxx[maxn]; inline void add(int u,int v,int w) { tot++; edge[tot].nxt=head[u]; edge[tot].w=w; edge[tot].to=v; head[u]=tot; } inline void dfs(int x) { minn[tmp]=min(minn[tmp],dis[x]); maxx[tmp]=max(maxx[tmp],dis[x]); vis[x]=1; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(!vis[v]) { dis[v]=dis[x]+edge[i].w; dfs(v); } else ans=__gcd(ans,abs(dis[x]+edge[i].w-dis[v])); } } inline int father(int x) { if(fath[x]!=x) return fath[x]=father(fath[x]); else return x; } inline void Union(int x,int y) { int f1=father(x),f2=father(y); if(f1!=f2) fath[f1]=f2; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) fath[i]=i; for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v,1),add(v,u,-1); Union(u,v); } for(int i=1;i<=n;i++) minn[i]=1e9; for(int i=1;i<=n;i++) if(!vis[i]) { tmp=father(i); dfs(i); } int ans1=0,ans2=0; if(ans!=0) { if(ans<3) { printf("-1 -1\n"); return 0; } ans1=ans; for(int i=3;i<=ans;i++) if(ans%i==0) { ans2=i; break; } printf("%d %d",ans1,ans2); return 0; } ans2=3; for(int i=1;i<=n;i++) if(fath[i]==i) ans1+=maxx[i]-minn[i]+1; if(ans1<3) printf("-1 -1"); else printf("%d %d",ans1,ans2); return 0; }
[NOI2008]设计路线
一眼树形DP,方程不再赘述,乘法原理。
经过优化:
一遍dfs即可
(图取自https://www.cnblogs.com/Randolph87/p/3679223.html)
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1e5+10; const int maxm=1e6+10; struct point { int to; int nxt; }edge[maxm*2]; int n,m,tot; int head[maxn]; long long f[maxn][24][3],f1,f2,mod; inline void add(int u,int v) { tot++; edge[tot].nxt=head[u]; edge[tot].to=v; head[u]=tot; } inline long long MOD(long long x) { if(x>0 && x%mod==0) return mod; return x%mod; } inline void dfs(int x,int fa) { for(int i=0;i<=12;i++) f[x][i][0]=1; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(v==fa) continue; dfs(v,x); for(int j=0;j<=12;j++) { f1=0,f2=0; if(j) f1=MOD(MOD(f[v][j-1][0]+f[v][j-1][1])+f[v][j-1][2]); f2=MOD(f[v][j][0]+f[v][j][1]); f[x][j][2]=MOD(MOD(f[x][j][2]*f1)+MOD(f[x][j][1]*f2)); f[x][j][1]=MOD(MOD(f[x][j][1]*f1)+MOD(f[x][j][0]*f2)); f[x][j][0]=MOD(f[x][j][0]*f1); } } } int main() { scanf("%d%d%lld",&n,&m,&mod); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v),add(v,u); } if(m<n-1) { printf("-1\n-1"); return 0; } dfs(1,1); int ans=0; long long ans1=0; for(int i=0;i<=12;i++) { if(f[1][i][0]+f[1][i][1]+f[1][i][2]>0) { ans=i; ans1=((f[1][i][0]+f[1][i][1])%mod+f[1][i][2])%mod; break; } } printf("%d\n%lld\n",ans,ans1); return 0; }
[NOI2008]志愿者招募
线性规划:
于是我们有了M个等式,利用流量平衡,建M个点表示M个等式; (第二个图片中是 —y[i])
1、从x[i]为正的向x[i]为负的等式建一条cap=inf,cost=c[i]的边
2、a[i]-a[i-1]为正的从S补流,为负的向T引流
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define inf 1e8 using namespace std; const int maxn=1e5+10; struct point { int to; int nxt; }edge[maxn]; int n,m,tot,S,T; int a[maxn]; int head[maxn],cap[maxn],flow[maxn]; int dis[maxn],vis[maxn],cost[maxn],pre[maxn],id[maxn]; inline void add(int u,int v,int c,int f) { edge[tot].nxt=head[u]; edge[tot].to=v; cap[tot]=c; cost[tot]=f; head[u]=tot; tot++; edge[tot].nxt=head[v]; edge[tot].to=u; cap[tot]=0; cost[tot]=-f; head[v]=tot; tot++; } bool Bfs() { deque<int> q; for(int i=0;i<=T;i++) dis[i]=inf; memset(vis,0,sizeof(vis)); vis[S]=1; dis[S]=0; q.push_back(S); while(!q.empty()) { int tt=q.front(); q.pop_front(); vis[tt]=0; for(int i=head[tt];~i;i=edge[i].nxt) { int v=edge[i].to; if(dis[v]>dis[tt]+cost[i] && cap[i]) { dis[v]=dis[tt]+cost[i]; if(!vis[v]) { vis[v]=1; if(q.empty() || dis[v]>dis[q.front()]) q.push_back(v); else q.push_front(v); } } } } if(dis[T]==inf) return false; return true; } int dfs(int x,int low) { vis[x]=1; if(x==T || low==0) return low; int res=0; for(int i=head[x];~i;i=edge[i].nxt) { int v=edge[i].to; if(!vis[v] && dis[x]+cost[i]==dis[v] && cap[i]>0) { int w=dfs(v,min(low,cap[i])); low-=w; res+=w; cap[i]-=w; cap[i^1]+=w; } } return res; } inline int Mincost() { int cc=0; while(Bfs()) { vis[T]=1; while(vis[T]) { memset(vis,0,sizeof(vis)); cc+=dfs(S,inf)*dis[T]; } } return cc; } int main() { // freopen("aa.in","r",stdin); scanf("%d%d",&n,&m); S=0,T=n+2; memset(head,-1,sizeof(head)); for(int i=1;i<=n+1;i++) { if(i<=n) scanf("%d",&a[i]); int cc=a[i]-a[i-1]; if(cc<0) add(i,T,-cc,0); //(i) x[i]+...-y[i]=cc else add(S,i,cc,0); //(i) x[i]+...-y[i]+cc=0 if(i>1) add(i,i-1,inf,0); //(i-1) x[i-1]-...-y[i-1] //(i) x[i]+...-y[i]+y[i-1] } for(int i=1;i<=m;i++) { int l,r,c; scanf("%d%d%d",&l,&r,&c); add(l,r+1,inf,c); //(l) x[i]+...-y[l]=cc //... //(r) x[i]-x[i]+...-y[r]=cc //(r+1) x[i+1]-x[i]+..-y[i+1]=cc } printf("%d",Mincost()); return 0; }