Instant Noodles呃 ԅ(¯﹃¯ԅ)看了眼题解,懂了吧就根据三种情况来判断
快!看!这个合并,其实是很简单的,就如果重复了,合并,如果没用重复,分两种情况讨论一下即可(虽然最终的殊途同归)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,len=0,last[500001],c[500001];
set<int > g[500001];
map<set<int >,int > mp;
signed main()
{
int t;scanf("%lld",&t);
while(t--)
{
mp.clear();
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<=n;i++) g[i].clear();
while(m--)
{
int x,y;scanf("%lld%lld",&x,&y);
g[y].insert(x);
}
for(int i=1;i<=n;i++)
{
if(g[i].size()) mp[g[i]]+=c[i];
}
int ans=0;
for(auto &i:mp)
if(ans) ans=__gcd(ans,i.second);
else ans=i.second;
printf("%lld\n",ans);
}
return 0;
}
这一拳,天昏地暗。Weights Distributing,迷茫起来了吗?一切还好吗?也许再也难见,便也笑着再见了。好!说题目,不知道是什么的图论,求 a 到 b 的最短路与 b 到 c 的最短路的和的最小值。不妨想想只有两个点的情况,那么简单嘛。再看看三个点,那就麻烦了因为会有重叠的情况,那么画出路径,大概是a->i->b,b->i->c,这样的,枚举i这样最小值就是
那么就可以放心维护了,最好搞一个前缀和什么的方便维护。直接统计路径来干就行,理解就好。
不过这个前缀和非常奇妙,学习一下?呃因为这题卡常我也没办法,先抄了别人的题解,我代码应该是没问题的。不过t了。
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[100001],w[100001],q[100001];
int dis[100001],disa[100001],disb[100001],disc[100001];
struct pp
{
int x,y,next;
};pp p[4000001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void bfs(int ST,int op)
{
memset(dis,0,sizeof(dis));dis[ST]=1;
int st=1,ed=2;q[st]=ST;
while(st!=ed)
{
int x=q[st++];
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(dis[y]) continue ;
dis[y]=dis[x]+1;q[ed++]=y;
}
}
if(op==1) for(int i=1;i<=n;i++) disa[i]=dis[i]-1;
if(op==2) for(int i=1;i<=n;i++) disb[i]=dis[i]-1;
if(op==3) for(int i=1;i<=n;i++) disc[i]=dis[i]-1;
return ;
}
bool cmp(const int &x,const int &y)
{
return x<y;
}
int main()
{
int t;scanf("%d",&t);
while(t--)
{
memset(last,-1,sizeof(last));len=0;
int a,b,c;scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
for(int i=1;i<=m;i++) scanf("%d",&w[i]);
sort(w+1,w+m+1,cmp);
for(int i=2;i<=m;i++) w[i]+=w[i-1];
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
bfs(a,1);bfs(b,2);bfs(c,3);int ans=1e9;
for(int i=1;i<=n;i++)
{
int A=disa[i],B=disb[i],C=disc[i];
if(A+B+C<=m) ans=min(ans,w[A+B+C]+w[B]);
}
printf("%d\n",ans);
}
return 0;
}
String Transformation 2,呃非常奇妙的做法,真的。https://blog.csdn.net/sharp_legendgod/article/details/119521342可以考虑一下看大佬的题解
题是写了,想法以后能补再补吧。
#include <bits/stdc++.h>
using namespace std;
//#define Fread
// #define Getmod
#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#endif // Fread
template <typename T>
void r1(T &x) {
x = 0;
char c(getchar());
int f(1);
for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
x *= f;
}
const int mod = 1e9 + 7;
#ifdef Getmod
template <int mod>
struct typemod {
int z;
typemod(int a = 0) : z(a) {}
inline int inc(int a,int b) const {return a += b - mod, a + ((a >> 31) & mod);}
inline int dec(int a,int b) const {return a -= b, a + ((a >> 31) & mod);}
inline int mul(int a,int b) const {return 1ll * a * b % mod;}
typemod<mod> operator + (const typemod<mod> &x) const {return typemod(inc(z, x.z));}
typemod<mod> operator - (const typemod<mod> &x) const {return typemod(dec(z, x.z));}
typemod<mod> operator * (const typemod<mod> &x) const {return typemod(mul(z, x.z));}
typemod<mod>& operator += (const typemod<mod> &x) {*this = *this + x; return *this;}
typemod<mod>& operator -= (const typemod<mod> &x) {*this = *this - x; return *this;}
typemod<mod>& operator *= (const typemod<mod> &x) {*this = *this * x; return *this;}
int operator == (const typemod<mod> &x) const {return x.z == z;}
int operator != (const typemod<mod> &x) const {return x.z != z;}
};
typedef typemod<mod> Tm;
#endif
template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
r1(t); r1(args...);
}
// #define int long long
const int maxn = 20 + 5;
const int maxm = 1e5 + 5;
int vis[maxn];
int n;
char A[maxm], B[maxm];
int vc1[maxn][maxn];
void add(int u,int v) {
++ u, ++ v;
vc1[u][v] = 1;
}
int vc[maxn][maxn];
int st[maxn], ed(0);
int E[maxn];
void dfs(int p) {
vis[p] = 1, st[ed ++] = p;
for(int j = 1; j <= 20; ++ j) if(vc1[p][j] && !vis[j]) dfs(j);
}
#define Online
signed main() {
#ifndef Online
freopen("S.in", "r", stdin);
freopen("S.out", "w", stdout);
#endif
auto calc = [&] (void) {
const int inf = 0x3f3f3f3f;
int z = (1 << ed) - 1, i, j;
for(i = 0; i < ed; ++ i) {
E[i] = 0;
for(j = 0; j < ed; ++ j) E[i] |= vc[st[i]][st[j]] << j;
}
vector<int> dp(z + 1, inf);
dp[0] = 0;
for(int S = 0; S <= z; ++ S) {
for(int j = 0; j < ed; ++ j) if(!((S >> j) & 1)) {
dp[S | (1 << j)] = min(dp[S | (1 << j)], dp[S] + !!(S & E[j]));
}
}
// printf("dp[%lld] = %lld\n", ed, dp[z]);
return dp[z] + ed - 1;
};
auto Solve = [&] (void) {
int i, j;
memset(vis, 0, sizeof(vis));
memset(vc, 0, sizeof(vc));
memset(vc1, 0, sizeof(vc1));
r1(n);
scanf("%s%s", A + 1, B + 1);
for(i = 1; i <= n; ++ i) {
vc[A[i] - 'a' + 1][B[i] - 'a' + 1] = 1;
add(A[i] - 'a', B[i] - 'a'), add(B[i] - 'a', A[i] - 'a');
}
int ans(0);
for(i = 1; i <= 20; ++ i) if(!vis[i]) {
ed = 0;
dfs(i);
ans += calc();
// printf("i = %lld\n", i);
}
printf("%d\n", ans);
// puts("----\n");
};
int T;
r1(T);
while(T --) Solve();
return 0;
}
/*
1
3
abc
tsr
3
*/
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[5020001];
bool v[5020001];
struct pp
{
int x,y,next;
};pp p[1020001];
struct node
{
int t,id;
};node e[5020001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
bool cmp(const node &x,const node &y)
{
return x.t<y.t;
}
int main()
{
memset(last,-1,sizeof(last));memset(v,true,sizeof(v));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
for(int i=1;i<=n;i++) scanf("%d",&e[i].t),e[i].id=i;
for(int i=1;i<=n;i++)
{
for(int j=last[i];j!=-1;j=p[j].next) v[e[p[j].y].t]=false ;
for(int j=1;j<e[i].t;j++)
{
if(v[j])
{
printf("-1");
return 0;
}
}
if(!v[e[i].t])
{
printf("-1");
return 0;
}
for(int j=last[i];j!=-1;j=p[j].next) v[e[p[j].y].t]=true ;
}
sort(e+1,e+n+1,cmp);
for(int i=1;i<=n;i++) printf("%d ",e[i].id);
return 0;
}
告一段落!
不不不第二阶段重启!P1948 [USACO08JAN]Telephone Lines S
//一看,求最大值最小,不妨考虑二分答案。然后用dj维护一下,若一条边的长度超过了上限
//那么计数器++最后判断计数器<=k就好了
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,len=0,last[100001],dis[100001],k;
struct pp
{
int x,y,c,next;
};pp p[1000001];
void ins(int x,int y,int c)
{
int now=++len;
p[now]={x,y,c,last[x]};last[x]=now;
return ;
}
int q[100001];bool v[100001];
bool spfa(int ST,int ED,int lim)
{
memset(v,true,sizeof(v));memset(dis,63,sizeof(dis));dis[ST]=0;
int st=1,ed=2;q[st]=ST;
while(st!=ed)
{
int x=q[st++];v[x]=true;
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y,s=dis[x];if(p[i].c>lim) s++;
if(dis[y]<=s) continue ;
dis[y]=s;if(v[y]) q[ed++]=y,v[y]=false;
}
}
if(dis[ED]==dis[0])
{
printf("-1");exit(0);
}
if(dis[ED]<=k) return true;
else return false;
}
main()
{
memset(last,-1,sizeof(last));
scanf("%lld%lld%lld",&n,&m,&k);int l=0,r=0,ans,ST=1,ED=n;
for(int i=1;i<=m;i++)
{
int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
ins(x,y,c);ins(y,x,c);r=max(r,c);
}
while(l<=r)
{
int mid=(l+r)/2;
if(spfa(ST,ED,mid)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("%lld",ans);
return 0;
}
//呃考虑从建边入手,不妨将婚姻的人g->b,情人的b->g,那么就可以得到一个有向有环图。
//那么再一个联通块里面的点,那么肯定可以换npy了,反之则不行
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[100001],low[100001],dfsx[100001],dfs_x=0,id[100001],point=0;
int q[100001],tot=0;bool v[100001];
map<string ,int > mp;
struct pp
{
int x,y,next;
};pp p[4000001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void SH(int x)
{
dfsx[x]=low[x]=++dfs_x;
v[x]=true;q[++tot]=x;
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;
if(!dfsx[y]) SH(y),low[x]=min(low[x],low[y]);
else if(v[y]) low[x]=min(low[x],dfsx[y]);
}
if(dfsx[x]==low[x])
{
point++;
while(dfsx[q[tot]]>=dfsx[x])
{
id[q[tot]]=point;v[q[tot]]=false;
tot--;dfs_x--;
}
}
return ;
}
int main()
{
memset(last,-1,sizeof(last));memset(v,false,sizeof(v));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
string s1,s2;cin>>s1>>s2;
mp[s1]=i,mp[s2]=i+n;ins(i,i+n);
}
scanf ("%d",&m);
for(int i=1;i<=m;i++)
{
string s1,s2;cin>>s1>>s2;
ins(mp[s2],mp[s1]);
}
for(int i=1;i<=n*2;i++) if(!dfsx[i]) SH(i);
for(int i=1;i<=n;i++)
{
if(id[i]==id[i+n]) printf("Unsafe\n");
else printf("Safe\n");
}
return 0;
}
P1640 [SCOI2010] 连续攻击游戏二分图的优化版,可以尝试用时间戳来优化memset,具体而言,设一个变量now,如果v里值为now则已访问过,若不是则赋值为now向下递归,这样就不用memset v数组了。然后二分图的思路是挺好想的因为要求必须从1开始,所以我们直接从1向上枚举即可。
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[2000001],v[2000001],cnt=0,op[2000001];
struct pp
{
int x,y,next;
};pp p[8000001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
bool dfs(int x)
{
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(v[y]==cnt) continue ;v[y]=cnt;
if(!op[y]||dfs(op[y]))
{
op[y]=x,op[x]=y;
return true ;
}
}
return false;
}
int main()
{
memset(last,-1,sizeof(last));memset(v,0,sizeof(v));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=2;j++)
{
int x;scanf("%d",&x);
ins(x,i+100001);ins(i+100001,x);
}
}int ans=0;
for(int i=1;i<=100001;i++)
{
cnt++;
if(dfs(i)) ans++;
else break;
}printf("%d",ans);
return 0;
}
矩阵加速弗洛伊德,问题在于转化负权,我们不如直接看题解。P6190 [NOI Online #1 入门组] 魔法
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,len,f[521][521];
struct pp
{
int x,y,c;
};pp p[1000001];
struct node
{
int n,m,w[121][121];
friend node operator *(const node &x,const node &y)
{
node rt;rt.n=x.n,rt.m=y.m;memset(rt.w,0x3f,sizeof(rt.w));
for(int i=1;i<=rt.n;i++)
{
for(int j=1;j<=rt.m;j++)
{
for(int k=1;k<=x.m;k++) rt.w[i][j]=min(rt.w[i][j],x.w[i][k]+y.w[k][j]);//printf("%d\n",rt.w[i][j]);
}
}
return rt;
};
};node A,B;
void init(node &now,int x)
{
now.n=now.m=x;memset(now.w,63,sizeof(now.w));
for(int i=1;i<=x;i++) now.w[i][i]=0;
return ;
}
node power(node x,int k)
{
node rt;init(rt,n);
while(k)
{
if(k&1) rt=rt*x;
x=x*x;k>>=1;
}
return rt;
}
signed main()
{
memset(f,0x3f,sizeof(f));
int k;scanf("%lld%lld%lld",&n,&m,&k);A.n=A.m=n;
for(int i=1;i<=m;i++)
{
int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
p[i].x=x,p[i].y=y,p[i].c=c;f[x][y]=c;
}
for(int i=1;i<=n;i++) f[i][i]=0;
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
memset(A.w,0x3f,sizeof(A.w));
for(int i=1;i<=m;i++)
{
int x=p[i].x,y=p[i].y,c=p[i].c;
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++) A.w[j][k]=min(A.w[j][k],min(f[j][k],f[j][x]+f[y][k]-c));
}
}
// for(int i=1;i<=n;i++) A.w[i][i]=0;
if(!k) printf("%lld\n",f[1][n]);
else
{
node ans=power(A,k);
printf("%lld\n",ans.w[1][n]);
}
return 0;
}
P1606 [USACO07FEB]Lilypad Pond G,先做一道前置的最短路计数题目P1608 路径统计,不难就当复习了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,len=0,last[100001],dis[10001],ans[100001];bool v[10001],mp[2301][2301];
struct pp
{
int x,y,c,next;
};pp p[4000001];
void ins(int x,int y,int c)
{
int now=++len;
p[now]={x,y,c,last[x]};last[x]=now;
return ;
}
struct node
{
int x,dis;friend bool operator < (const node &x,const node &y)
{
return x.dis > y.dis;
};
};priority_queue<node > q;
bool dj(int ST,int ED)
{
memset(dis,63,sizeof(dis));memset(v,true,sizeof(v));memset(ans,0,sizeof(ans));dis[ST]=0;
node e;e.dis=dis[ST];e.x=ST;q.push(e);ans[ST]=1;
while(q.size())
{
node x=q.top();q.pop();if(!v[x.x]) continue ;v[x.x]=false;
for(int i=last[x.x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(dis[y]<dis[x.x]+p[i].c) continue ;
if(dis[y]==dis[x.x]+p[i].c) ans[y]+=ans[x.x];
else ans[y]=ans[x.x],dis[y]=dis[x.x]+p[i].c,e.dis=dis[y],e.x=y,q.push(e);
}
}
if(dis[0]==dis[ED]) return false;
else return true ;
}
main()
{
memset(last,-1,sizeof(last));memset(mp,true,sizeof(mp));
scanf("%lld%lld",&n,&m);
for(int k=1;k<=m;k++)
{
int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
if(mp[x][y]) ins(x,y,c),mp[x][y]=false;
else
{
for(int i=last[x];i!=-1;i=p[i].next)
{
int yy=p[i].y;if(y!=yy) continue ;
p[i].c=min(p[i].c,c);break;
}
}
}
if(!dj(1,n)) printf("No answer");
else printf("%lld %lld\n",dis[n],ans[n]);
return 0;
}
好的我们选择跳过上面那道题:P4042 [AHOI2014/JSOI2014]骑士游戏加深一下对spfa的理解。好吧其实不看题解的话我不打算用spfa做的,我应该先考虑拓扑,发现不行,然后考虑dp或者记忆化。仔细想想,这一题需要松弛。
//不妨尝试用改版的spfa,如果我儿子节点的值被更新后能更新我自己,那么我就更新我的父亲
//但是这样的时间复杂度难以保证
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,len=0,last[500001],dis[500001],a[500001],lastfa[500001];
struct pp
{
int x,y,next;
};pp fa[2000001],p[2000001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void insfa(int x,int y)
{
int now=++len;
fa[now]={x,y,lastfa[x]};lastfa[x]=now;
return ;
}
int q[1000001];bool v[500001];
void spfa()
{
int st=1,ed=1;
for(int i=1;i<=n;i++) q[ed++]=i;
while(st!=ed)
{
int x=q[st++];v[x]=false;
int tmp=a[x];
for(int i=last[x];i!=-1;i=p[i].next) tmp+=dis[p[i].y];
if(dis[x]>tmp)
{
dis[x]=tmp;
for(int i=lastfa[x];i!=-1;i=fa[i].next)
{
if(!v[fa[i].y]) q[ed++]=fa[i].y,v[fa[i].y]=true;
}
}
}
return ;
}
main()
{
memset(last,-1,sizeof(last));memset(lastfa,-1,sizeof(lastfa));
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
int x;scanf("%lld%lld%lld",&a[i],&dis[i],&x);
while(x--)
{
int y;scanf("%lld",&y);
ins(i,y),insfa(y,i);
}
}
spfa();printf("%lld",dis[1]);
return 0;
}
MST Unificationluogu炸了所以换了个地方交
//求解唯一最小生成树,我们考虑一遍建树一遍做,那么不妨观察对于一个树边,要是能被替代的话肯定是有另一条有相同值且可以与其构成环的边
//那么我们的解决方案是让那个边的值加一,所以我们只需要在寻找的过程中,一边建树一边算.
//先算相同边的数量,若之前就在同一个块里面那就不统计
//然后再算组成环的数量,那么就要减去一部分。
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,maxx[1000001],fa[1000001],ans=0;
struct pp
{
int x,y,c;
};pp p[1000001];
bool cmp(const pp &x,const pp &y)
{
return x.c<y.c;
}
int findfa(int x)
{
if(fa[x]==x) return x;
return fa[x]=findfa(fa[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].c);
for(int i=1;i<=n;i++) fa[i]=i;sort(p+1,p+m+1,cmp);
int i=1;
while(i<=m)
{
int j=i;while(p[j].c==p[j+1].c) j++;
for(int k=i;k<=j;k++) if(findfa(p[k].x)!=findfa(p[k].y)) ans++;
for(int k=i;k<=j;k++)
{
int fx=findfa(p[k].x),fy=findfa(p[k].y);
if(fx!=fy) fa[fx]=fy,ans--;
}i=j+1;
}
printf("%d",ans);
return 0;
}