luoguP2243 电路维修
权值仅0,1,即可O(1)维护单调队列做dijkstra。用双端队列实现。
// luogu-judger-enable-o2
//Achen
#include
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=507;
typedef long long LL;
typedef double db;
using namespace std;
int T,n,m,d[N][N],tx[5]={-1,-1,1,1},ty[5]={-1,1,-1,1};
char s[N][N];
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
#define pr pair
#define fi first
#define se second
#define MP make_pair
int ck(int x,int y) {
return x>=1&&x<=n&&y>=1&&y<=m;
}
deque<pr>que;
void solve() {
n++; m++;
memset(d,127/3,sizeof(d));
int inf=d[1][1];
d[1][1]=0;
que.push_back(MP(1,1));
while(!que.empty()) {
pr t=que.front();
que.pop_front();
int x=t.fi,y=t.se;
For(i,0,3) if(ck(x+tx[i],y+ty[i])) {
int xx=x+tx[i],yy=y+ty[i],c=0;
if((i==0&&s[xx][yy]=='/')||(i==3&&s[x][y]=='/')||(i==1&&s[x-1][y]=='\\')||(i==2&&s[x][y-1]=='\\')) c=1;
if(d[xx][yy]>d[x][y]+c) {
d[xx][yy]=d[x][y]+c;
if(!c) que.push_front(MP(xx,yy));
else que.push_back(MP(xx,yy));
}
}
}
printf("%d\n",d[n][m]);
}
//#define ANS
int main() {
#ifdef ANS
freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
#endif
read(T);
while(T--) {
read(n); read(m);
For(i,1,n) scanf("%s",s[i]+1);
if((n%2==0&&m%2==1)||(n%2==1&&m%2==0)) puts("NO SOLUTION");
else solve();
}
Formylove;
}
传送门
网格图多次询问最短路,每次考虑同一个矩形内的询问的答案,从矩形长边的中点切一刀,从中间这条线上的点跑最短路,更新所有询问的答案再递归下去。
很久前的丑陋代码
//Achen
#include
#include
#include
#include
#include
#include
#include
#include
const int N=2e5+7;
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
typedef long long LL;
using namespace std;
int n,m,q,ans[N];
template<typename T>void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int ecnt,fir[N],nxt[N<<1],to[N<<1],val[N];
void add(int u,int v,int w) {
nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;
}
int get(int x,int y) { return (x-1)*m+y; }
void get(int xx,int &x,int &y) { y=xx%m; if(!y) y=m; x=xx/m; if(xx%m) x++; }
int in(int x,int y,int l,int r,int L,int R) { return x>=l&&x<=r&&y>=L&&y<=R; }
struct node {
int x,y,xx,yy,id;
}qs[N],lq[N],rq[N];
int dis[N],vis[N];
struct dj {
int x,dis;
dj(int x,int dis):x(x),dis(dis){}
friend bool operator <(const dj&A,const dj&B) {
return A.dis>B.dis;
}
};
priority_queue<dj>que;
void dijkstra(int s,int l,int r,int L,int R) {
For(i,l,r) For(j,L,R) {
int x=get(i,j);
dis[x]=ans[0]; vis[x]=0;
}
dis[s]=0;
while(!que.empty()) que.pop();
que.push(dj(s,0));
while(!que.empty()) {
dj x=que.top();
que.pop();
if(vis[x.x]||x.dis!=dis[x.x]) continue;
vis[x.x]=1;
for(int i=fir[x.x];i;i=nxt[i]) if(!vis[to[i]]) {
int xx,yy; get(to[i],xx,yy);
if(in(xx,yy,l,r,L,R)) {
if(dis[to[i]]>dis[x.x]+val[i]) {
dis[to[i]]=dis[x.x]+val[i];
que.push(dj(to[i],dis[to[i]]));
}
}
}
}
}
void solve(int l,int r,int L,int R,int ql,int qr) {
if(l>r||L>R||ql>qr) return;
if(r-l<=R-L) {
int mid=((L+R)>>1),ll=0,rr=0;
For(i,l,r) {
dijkstra(get(i,mid),l,r,L,R);
For(j,ql,qr) {
ans[qs[j].id]=min(ans[qs[j].id],dis[get(qs[j].x,qs[j].y)]+dis[get(qs[j].xx,qs[j].yy)]);
if(i==l) {
if(in(qs[j].x,qs[j].y,l,r,L,mid)&&in(qs[j].xx,qs[j].yy,l,r,L,mid)) lq[++ll]=qs[j];
else if(in(qs[j].x,qs[j].y,l,r,mid+1,R)&&in(qs[j].xx,qs[j].yy,l,r,mid+1,R)) rq[++rr]=qs[j];
}
}
}
For(i,1,ll) qs[ql+i-1]=lq[i]; For(i,1,rr) qs[ql+ll+i-1]=rq[i];
if(l==r||L==R) return;
solve(l,r,L,mid,ql,ql+ll-1); solve(l,r,mid+1,R,ql+ll,ql+ll+rr-1);
}
else {
int mid=((l+r)>>1),ll=0,rr=0;
For(i,L,R) {
dijkstra(get(mid,i),l,r,L,R);
For(j,ql,qr) {
ans[qs[j].id]=min(ans[qs[j].id],dis[get(qs[j].x,qs[j].y)]+dis[get(qs[j].xx,qs[j].yy)]);
if(i==L) {
if(in(qs[j].x,qs[j].y,l,mid,L,R)&&in(qs[j].xx,qs[j].yy,l,mid,L,R)) lq[++ll]=qs[j];
else if(in(qs[j].x,qs[j].y,mid+1,r,L,R)&&in(qs[j].xx,qs[j].yy,mid+1,r,L,R)) rq[++rr]=qs[j];
}
}
}
For(i,1,ll) qs[ql+i-1]=lq[i]; For(i,1,rr) qs[ql+ll+i-1]=rq[i];
if(l==r||L==R) return;
solve(l,mid,L,R,ql,ql+ll-1); solve(mid+1,r,L,R,ql+ll,ql+ll+rr-1);
}
}
//#define DEBUG
int main() {
#ifdef DEBUG
freopen("4456.in","r",stdin);
freopen("4456.out","w",stdout);
#endif
read(n); read(m);
For(i,1,n)
For(j,1,m-1) {
int w; read(w);
add(get(i,j),get(i,j+1),w);
}
For(i,1,n-1)
For(j,1,m) {
int w; read(w);
add(get(i,j),get(i+1,j),w);
}
read(q);
For(i,1,q) {
read(qs[i].x); read(qs[i].y);
read(qs[i].xx); read(qs[i].yy);
qs[i].id=i;
}
memset(ans,127/3,sizeof(ans));
solve(1,n,1,m,1,q);
For(i,1,q) printf("%d\n",ans[i]);
return 0;
}
/*
2
3
4
1 1 2 2
2 2 1
*/
传送门
求s到t至少经过k条边的最短路,多组询问,n<=50,m<=1e4,q<=1e5.
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示从i走k步到j的最短路
g [ i ] [ j ] [ k ] g[i][j][k] g[i][j][k]表示从i走k*100步到j的最短路
h [ i ] [ j ] [ k ] h[i][j][k] h[i][j][k]表示从i至少走k步到j的最短路
询问时,因为至多会多走n步,用上面预处理的f,g,h可以得出答案。
//Achen
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define Formylove return 0
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=55;
typedef long long LL;
typedef double db;
using namespace std;
int T,n,m,f[N][N][205],g[N][N][205],h[N][N][205];
template<typename T>void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int main() {
#ifdef ANS
freopen(".in","r",stdin);
freopen(".out","w",stdout);
#endif
read(T);
while(T--) {
read(n); read(m);
memset(f,127/3,sizeof(f));
memset(g,127/3,sizeof(g));
memset(h,127/3,sizeof(h));
int inf=f[0][0][0];
For(i,1,n) f[i][i][0]=g[i][i][0]=h[i][i][0]=0;
For(i,1,m) {
int u,v,w;
read(u); read(v); read(w);
f[u][v][1]=min(f[u][v][1],w);
}
For(k,2,200) {
For(l,1,n) For(i,1,n) For(j,1,n)
f[i][j][k]=min(f[i][j][k],f[i][l][k-1]+f[l][j][1]);
}
For(i,1,n) For(j,1,n) g[i][j][1]=f[i][j][100];
For(k,2,100)
For(l,1,n) For(i,1,n) For(j,1,n)
g[i][j][k]=min(g[i][j][k],g[i][l][k-1]+g[l][j][1]);
For(i,1,n) For(j,1,n) For(k,1,100)
For(l,k,200) h[i][j][k]=min(h[i][j][k],f[i][j][l]);
int q;
read(q);
while(q--) {
int i,j,z;
read(i); read(j); read(z);
int ans=inf;
For(k,1,n) {
int a=z/100,b=z%100;
if(b==0) a--,b=100;
ans=min(ans,g[i][k][a]+h[k][j][b]);
}
if(ans>=inf) ans=-1;
printf("%d\n",ans);
}
}
Formylove;
}
路径权值定义为经过点权的最大值乘边权最大值,求两两点对间最短路。
考虑 floyd 的本质: f [ k ] [ i ] [ j ] f[k][i][j] f[k][i][j]表示只经过前 k 个点的情况下 i 到 j 的最短路,一般按节点标号顺序枚举 k 即可求得两两最短路。这里我 们可以改变枚举顺序,按点权从小到大枚举 k,这样 f [ k ] [ i ] [ j ] f[k][i][j] f[k][i][j]就是点权 不超过第 k 个点,i 到 j 的最短路,在每个阶段都更新一遍两两点对 答案,时间复杂度 O ( n 3 ) O(n^3 ) O(n3)。
s到t关于k的路径权值定义为路径上前k大的边的权值和。求关于K=1~m的最短路。
容易想到枚举第k大的边v,把权值小于它的设为0。如果直接这样跑最短路,肯定会尽量跑那些0边导致根本就没选到k条大于等于v的边,也就是最短路上第k大小于v,一些需要计算代价的边被看成了0边,权值不真实。
我们想知道最短路上到底选了多少条大于等于v的边,此时所有非0边都大于等于v,最短路的关系等价于把所有非0边边权-v个后的图,且这样最后我们可以暴力枚举假设它选了k条大于等于v的边+k*v来更新k的答案。
显然当枚举的边数不对时k的答案不会更优。
且k的合法最优解的路径经过上诉对第k大边权值的处理时得到的一定是最短路(否则最短路可以构造出更优解或最优解直接是全部边的最短路),所以所有最优解会被枚举到会被枚举到。
问1到n的最短路长度+K(k<=50)以内的长度的路径条数,边权非负。
正反最短路+拓扑排序处理0环。
其实没什么可说的,主要是最后统计方案数dp时,排序不能傻乎乎地n*k的排序,dis为第一关键字,拓扑序为第二关键字排序n个点,k从小到大枚举来更新即可。
//Achen
#include
#include
#include
#include
#include
#include
#include
#include
#include
const int N=100005;
const int M=200005;
typedef long long LL;
using namespace std;
int T,n,m,k,p;
LL dp[N][51];
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int fir[N],nxt[M],to[M],val[M],ecnt;
int fif[N],nxf[M],tf[M],vaf[M],ecnf;
void add(int u,int v,int w) {
nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
nxf[++ecnf]=fif[v]; fif[v]=ecnf; tf[ecnf]=u; vaf[ecnf]=w;
}
int fi[N],nx[M],tt[M],in[N],ec;
void Add(int u,int v) {
nx[++ec]=fi[u]; fi[u]=ec; tt[ec]=v; in[v]++;
}
queue<int>que;
int ds[N],dt[N],vis[N],tps[N];
void spfa(int s,int d[],int fir[],int nxt[],int to[],int val[]) {
d[s]=0;
vis[s]=1;
que.push(s);
while(!que.empty()) {
int x=que.front();
que.pop();
vis[x]=0;
for(int i=fir[x];i;i=nxt[i])
if(d[to[i]]>d[x]+val[i]) {
d[to[i]]=d[x]+val[i];
if(!vis[to[i]]) {
vis[to[i]]=1;
que.push(to[i]);
}
}
}
}
int tpsort() {
for(int i=1;i<=n;i++) if(!in[i]) que.push(i);
int idd=0;
while(!que.empty()) {
int x=que.front();
que.pop();
tps[x]=++idd;
for(int i=fi[x];i;i=nx[i]) {
if(!(--in[tt[i]]))
que.push(tt[i]);
}
}
for(int i=1;i<=n;i++)
if(in[i]&&ds[i]+dt[i]<=ds[n]+k)
return 1;
return 0;
}
struct node{
int x;
friend bool operator <(const node &A,const node &B) {
return ds[A.x]==ds[B.x]?tps[A.x]<tps[B.x]:ds[A.x]<ds[B.x];
}
}po[N*50];
void clear() {
ecnt=ec=ecnf=0;
memset(dt,127,sizeof(dt));
memset(ds,127,sizeof(ds));
memset(fi,0,sizeof(fir));
memset(fir,0,sizeof(fir));
memset(fif,0,sizeof(fif));
memset(in,0,sizeof(in));
memset(tps,0,sizeof(tps));
memset(dp,0,sizeof(dp));
}
void work() {
spfa(1,ds,fir,nxt,to,val);
spfa(n,dt,fif,nxf,tf,vaf);
if(tpsort()) printf("-1\n");
else {
int tot=0;
for(int i=1;i<=n;i++)
po[++tot].x=i;
dp[1][0]=1;
sort(po+1,po+tot+1);
for(int y=0;y<=k;y++) {
for(int o=1;o<=tot;o++) {
int x=po[o].x;
for(int i=fir[x];i;i=nxt[i])
if((LL)ds[x]+y+val[i]+dt[to[i]]<=ds[n]+k) {
(dp[to[i]][ds[x]+y+val[i]-ds[to[i]]]+=dp[x][y])%=p;
}
}
}
LL ans=0;
for(int i=0;i<=k;i++) (ans+=dp[n][i])%=p;
printf("%lld\n",ans);
}
}
void init() {
read(T);
while(T--) {
clear();
read(n);
read(m);
read(k);
read(p);
for(int i=1;i<=m;i++) {
int u,v,w;
read(u); read(v); read(w);
if(!w) Add(u,v);
add(u,v,w);
}
work();
}
}
#define DEBUG
int main() {
#ifdef DEBUG
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
#endif
init();
return 0;
}
hdu5036Explosion
无关:此题题解虽然和图论没有关系还是说一下做法,有n个门每个门里有一些钥匙没把钥匙可以打开一些门,没有钥匙的时候就用炸弹随机炸一扇没开过的门,问期望用炸弹的个数。根据期望的可加性,答案就是每扇门被炸的次数的期望之和。如果有k扇门打开后就可以打开x这扇门(不一定是直接打开),那么x被炸的次数的期望就是1/(k+1)。
//Achen
#include
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=1007;
typedef long long LL;
typedef double db;
using namespace std;
int T,n;
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
bitset<N>b[N];
//#define ANS
int main() {
#ifdef ANS
freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
#endif
read(T);
For(cs,1,T) {
read(n);
For(i,1,n) {
b[i].reset();
b[i].set(i);
int k,x;
read(k);
For(j,1,k) {
read(x);
b[i].set(x);
}
}
For(k,1,n) For(i,1,n) if(b[i][k])
b[i]|=b[k];
db ans=0;
For(i,1,n) {
int cnt=0;
For(j,1,n) if(b[j][i]) cnt++;
ans+=1.0/(1.0*cnt);
}
printf("Case #%d: %.5lf\n",cs,ans);
}
Formylove;
}
传送门
这道当年sxy给我讲的题帮我过了Noip2017小凯的疑惑,Noip2018货币系统,感谢sxy让我苟延残喘到今天。
用最小的a把所有数按mod a分类,建出代表0~a-1的a个点,对每个ai,点x向点(x+ai)%a连边ai,跑最短路求得能表达出的mod a=x的最小值即可。
a,b互质,问ax+by不能组成的最大的数。
传送门
答案为ab-a-b.
设a 0 b , 1 b , 2 b , 3 b , . . . , ( a − 1 ) b ( m o d a ) 0b,1b,2b,3b,...,(a-1)b(mod \ a) 0b,1b,2b,3b,...,(a−1)b(mod a)的结果两两不同。
反证,若存在 x b = y b ( m o d a ) , ( 0 ≤ x < y < a ) xb=yb(mod \ a),(0 \leq x<y< a) xb=yb(mod a),(0≤x<y<a)
( y − x ) b = k a (y-x)b=ka (y−x)b=ka
∵ ( a , b ) = 1 , ∴ a ∣ ( y − x ) ∵(a,b)=1,∴a|(y-x) ∵(a,b)=1,∴a∣(y−x)
y − x < a y-x<a y−x<a矛盾。
故mod a=kb%a(k=0~(a-1))的最小数为kb且大于它的数都能构造出.最大不能构成的数即(a-1)*b-a=ab-a-b.
对于限制y<=x+a,从x向y连权值为a的边,通过最短路来满足这些限制。
即使已经过去两年了,Achen仍然忘不了当初啥都不知道的时候llj神仙第一次用spfa解一道约束问题时给Achen带来的震撼。
传送门
//Achen
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define Formylove return 0
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=607,M=100007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m1,m2;
int f[N][N];
template<typename T>void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int ecnt,fir[N],nxt[M<<1],to[M<<1];
void add(int u,int v) {
nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}
vector<int>vc[N];
int dfn[N],low[N],dfk,sta[N],top,bl[N],tot;
void tarjan(int x) {
dfn[x]=low[x]=++dfk;
sta[++top]=x;
for(int i=fir[x];i;i=nxt[i]) {
if(!dfn[to[i]]) {
tarjan(to[i]);
low[x]=min(low[x],low[to[i]]);
}
else if(!bl[to[i]]) low[x]=min(low[x],dfn[to[i]]);
}
if(dfn[x]==low[x]) {
bl[x]=++tot;
for(;;) {
int u=sta[top--];
vc[tot].push_back(u);
bl[u]=tot;
if(u==x) break;
}
}
}
//#define ANS
int main() {
#ifdef ANS
freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
#endif
read(n); read(m1); read(m2);
memset(f,127/3,sizeof(f));
For(i,1,n) f[i][i]=0;
For(i,1,m1) {
int x,y;
read(x); read(y);
add(x,y);
add(y,x);
f[x][y]=min(f[x][y],1);
f[y][x]=min(f[y][x],-1);
}
For(i,1,m2) {
int x,y;
read(x); read(y);
add(y,x);
f[y][x]=min(f[y][x],0);
}
For(k,1,n) For(i,1,n) For(j,1,n)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
For(i,1,n) if(f[i][i]<0) {
puts("NIE");
return 0;
}
For(i,1,n) if(!dfn[i]) tarjan(i);
int ans=0;
For(i,1,tot) {
int up=vc[i].size();
int mx=0;
For(j,0,up-1) For(k,0,up-1)
mx=max(mx,f[vc[i][j]][vc[i][k]]);
ans+=mx+1;
}
printf("%d\n",ans);
Formylove;
}
/*
4 2 2
1 2
3 4
1 4
3 1
*/