题太水了懒得一个一个发
T1:
题意就是5000000个点,10000000条边,边权只有1或2,求最短路
我的做法:
先BB一句,要开始考试的时候L让我和gigo,gsy去楼下考,我说看一下题再决定,然后他说:你看,第一题是裸的最短路。又因为我懒得搬东西去楼下并且不想承受爆零/垫底的压力,于是就留在楼上。
然后做这道题的时候。嗯...L都说了是最短路,直接上SPFA,觉得可能会卡我于是用了SLF优化,又写上了DZYOの快读,测了下极限数据大概3 4秒,应该不会被卡了
正解:
把边权为2的边拆成两条边权为1的边进行BFS。
然后好像发现我比标算跑得快
代码:
#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<18|1;
inline char nc()
{
static char ibuf[RLEN],*ib,*ob;
(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ib==ob) ? -1 : *ib++;
}
template<class T>
inline void read(T &x)
{
x=0; int f=1;
static char ch=nc();
while(!isdigit(ch)) ch=nc();
while(isdigit(ch)) x=x*10+ch-'0',ch=nc();
x*=f;
}
char xxx;
const int N=5000005;
const int M=10000005;
struct Edge
{
int to,next;
short val;
}edge[2*M];
int tot,first[N],n,m,t;
int dis[N];
bool inque[N];
char yyy;
inline void addedge(int x,int y,int z)
{
tot++;
edge[tot].to=y; edge[tot].next=first[x]; edge[tot].val=z; first[x]=tot;
}
inline void spfa_slf(int s,int t)
{
memset(dis,0x3f,sizeof(dis));
deque <int> q;
dis[s]=0; inque[s]=1; q.push_back(s);
while(!q.empty())
{
int now=q.front(); q.pop_front(); inque[now]=0;
if(now==t) break;
for(register int u=first[now];u;u=edge[u].next)
{
int vis=edge[u].to;
if(dis[now]+edge[u].val<dis[vis])
{
dis[vis]=dis[now]+edge[u].val;
if(!inque[vis])
{
inque[vis]=1;
if(!q.empty()&&dis[vis]<dis[q.front()]) q.push_front(vis);
else q.push_back(vis);
}
}
}
}
cout<<dis[t]<<endl;
}
int main()
{
freopen("short.in","r",stdin);
freopen("short.out","w",stdout);
read(n),read(m),read(t);
int u,v,w;
for(register int i=1;i<=m;i++)
{
read(u),read(v),read(w);
addedge(u,v,w);
addedge(v,u,w);
}
spfa_slf(1,t);
return 0;
}
T2:
题意:n 个字母,k条约束(x,y),表示x必须在y前面,问满足条件的全排列数
做法:直接全排列操过去
代码:
#include<bits/stdc++.h>
using namespace std;
int n,k,a[15];
char ch[15];
vector<int> b[15];
map<char,int> ha;
bool check() //验证a数组是否合法
{
for(int i=1;i<=n;i++)
for(int j=0;j<b[a[i]].size();j++)
{
int tar=b[a[i]][j];
int flag=0;
for(int p=i+1;p<=n;p++)
if(a[p]==tar) flag=1;
if(!flag) return false;
}
return true;
}
string s[100000];
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>ch[i],a[i]=i,ha[ch[i]]=i;
char x,y;
for(int i=1;i<=k;i++)
{
cin>>x>>y;
b[ha[x]].push_back(ha[y]);
}
int cnt=0;
do
{
if(check())
{
cnt++;
for(int i=1;i<=n;i++) s[cnt]+=ch[a[i]];
}
}while(next_permutation(a+1,a+n+1));
sort(s+1,s+cnt+1);
for(int i=1;i<=cnt;i++) cout<<s[i]<<endl;
return 0;
}
T3:
一个无向图,可以删除一个点及其出去的边,问删去哪些点后剩下的点是一棵树
对于 40%的数据:n<=1000,m<=1000; 另外存在 10%的数据:m=n-1; 另外存在 20%的数据:m=n; 对于 100%
的数据:1e5
我的做法:
保险起见,分了三档,对于m=n-1,就是一颗树,其中叶子节点可以删,对于m=n,相当于找出基环树的环上的点,对于其他的,找出那些m-出度=n-2的点,然后判联通
我以为我可以过的,结果naive了。其实不需要每次O(n)判联通,只需要判断这个点是不是割点即可,CNM。
分段代码:
#include<bits/stdc++.h>
const int N=100005;
const int M=100005;
using namespace std;
template<class T>
inline void read(T &x)
{
x=0; int f=1;
static char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
x*=f;
}
int n,m,tot=1,first[N],father[N],kp,dfn[N],idx,f[N];
struct Edge
{
int from,to,next,f;
}edge[2*M];
vector <int> p,ans,d;
inline void addedge(int x,int y)
{
tot++;
edge[tot].from=x; edge[tot].to=y; edge[tot].next=first[x]; first[x]=tot;
}
inline int getfather(int x)
{
if(father[x]==x) return x;
return father[x]=getfather(father[x]);
}
inline bool check(int key)
{
for(int i=1;i<=n;i++) father[i]=i;
for(int i=2;i<=tot;i++)
{
int u=edge[i].from,v=edge[i].to;
if(edge[i].f==1) continue;
int fx=getfather(u),fy=getfather(v);
if(fx==fy) continue;
father[fx]=fy;
}
int last;
if(key==1) last=getfather(2);
else last=getfather(1);
for(int i=1;i<=n;i++)
{
if(i==key) continue;
int fa=getfather(i);
if(fa!=last) return false;
last=fa;
}
return true;
}
void Solve1(int i)
{
for(int u=first[i];u;u=edge[u].next)
{
edge[u].f=1;
edge[u^1].f=1;
}
if(check(i)) ans.push_back(i);
for(int u=first[i];u;u=edge[u].next)
{
edge[u].f=0;
edge[u^1].f=0;
}
}
void dfs(int now,int fa)
{
int cnt=0;
for(int u=first[now];u;u=edge[u].next)
{
int vis=edge[u].to;
if(vis==fa) continue;
cnt++;
dfs(vis,now);
}
if(cnt==0) ans.push_back(now);
}
void dfs2(int now)
{
dfn[now]=++idx;
for(int u=first[now];u;u=edge[u].next)
{
int vis=edge[u].to;
if(vis==f[now]) continue;
if(dfn[vis])
{
if(dfn[vis]<dfn[now]) continue;
ans.push_back(vis);
for(;vis!=now;vis=f[vis]) ans.push_back(f[vis]);
}
else f[vis]=now,dfs2(vis);
}
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n),read(m);
for(int u,v,i=1;i<=m;i++)
{
read(u),read(v);
addedge(u,v);
addedge(v,u);
}
for(int i=1;i<=n;i++)
{
int cnt=0;
for(int u=first[i];u;u=edge[u].next) cnt++;
if(m-cnt==n-2)
p.push_back(i);
}
if(m==n-1) //树
{
for(int i=1;i<=n;i++)
{
int cnt=0;
for(int u=first[i];u;u=edge[u].next) cnt++;
if(cnt>1) {dfs(i,0);break;}
}
cout<<ans.size()<<'\n';
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
return 0;
}
else if(n==m) //基♂环树
{
dfs2(1);
for(int i=0;i<ans.size();i++)
{
int cnt=0;
for(int u=first[ans[i]];u;u=edge[u].next) cnt++;
if(cnt!=2) ans.erase(ans.begin()+i),i--;
}
cout<<ans.size()<<'\n';
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
return 0;
}
else
{
for(int i=0;i<p.size();i++) Solve1(p[i]);
cout<<ans.size()<<'\n';
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
return 0;
}
}
总结:
没什么总结,记忆犹新的就是最后几分钟对拍T2,结果发现出了点问题(没有输出),然后慌得一P,一度以为T2要爆零了,结果NM数据出错了。
下次还是去楼下找虐吧!