最小生成树
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵恰好有need条白色边的权值和最小的生成树。题目保证有解。
对于所有数据,V,E<=100000,c为[1,1000]中的正整数。
题解
可以知道恰好选到need条白边就是最优的,考虑给所有白边加上一个值,随着值的增大,在生成树中的白边越少,所以可以二分。
取最后一次满足条件的值。
排序的时候,对于相同值的边白边优先。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100005;
int n,m,need;
int fa[maxn],sum,cnt;
struct edge{
int x,y,c,val;
bool operator < (const edge a) const {
if(val==a.val) return c<a.c;
return val<a.val;
}
}e[maxn],cy[maxn];
template<class T>inline void read(T &x){
x=0;int f=0;char ch=getchar();
while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x = f ? -x : x ;
}
int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
bool check(int x){
sum=0,cnt=0;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
cy[i]=e[i];
if(!e[i].c) cy[i].val+=x;
}
sort(cy+1,cy+m+1);
for(int i=1;i<=m;i++){
int dx=find(cy[i].x),dy=find(cy[i].y);
if(dx!=dy){
fa[dx]=dy;
sum+=cy[i].val;
cnt+=!cy[i].c;
}
}
return cnt>=need;
}
int main(){
freopen("e.in","r",stdin);
freopen("e.out","w",stdout);
read(n);read(m);read(need);
for(int i=1;i<=m;i++) read(e[i].x),read(e[i].y),read(e[i].val),read(e[i].c);
int l=-1005,r=1005,ret;
while(l<=r){
int mid=(l+r)/2;
if(check(mid)) ret=sum-need*mid,l=mid+1;//写need
else r=mid-1;
}
printf("%d",ret);
}
很玄的一道题
贪吃蛇
有两条蛇(1 号蛇和 2 号蛇)在 n 行 m 列的地图上,地图上有障碍物。一条蛇碰到蛇身/障碍物/边界就会死。蛇身会不断长长——可以理解为蛇尾位置不会变,蛇只会向前伸展不会缩尾巴。两条蛇都绝顶聪明,如果自己能赢,一定会尽量快地赢;如果自己会输,一
定会死得尽量晚。给出初始局面,两蛇轮流走,每次可以且必须向上下左右移动一格。1 号蛇先走,请告诉我谁会在多少回合时赢。
对于100%的数据,1<=n<=20,1<=m<=20,0的个数不超过50个。
题解
用的是alpha-beta算法,还没怎么搞懂,就给个博客
代码T了两个点

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int inf=1000000;
const int maxn=25;
const int oo=400;
int n,m;
int mp[maxn][maxn];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
bool vis[maxn][maxn];
int maxmin(int step,int x1,int y1,int x2,int y2,int alpha,int beta){
bool opt=false;
if(step&1){//提高下界
for(int i=0;i<4;i++){
int xx=x1+dx[i],yy=y1+dy[i],val=alpha;
if(xx>0&&xx<=n&&yy>0&&yy<=m&&!vis[xx][yy]){
opt=true;
vis[xx][yy]=true;
val=maxmin(step+1,xx,yy,x2,y2,alpha,beta);
vis[xx][yy]=false;
}
if(val>alpha) alpha=val;
if(alpha>beta) return alpha;
}
if(opt) return alpha;
}
else {//缩小上界
for(int i=0;i<4;i++){
int xx=x2+dx[i],yy=y2+dy[i],val=beta;
if(xx>0&&xx<=n&&yy>0&&yy<=m&&!vis[xx][yy]){
opt=true;
vis[xx][yy]=true;
val=maxmin(step+1,x1,y1,xx,yy,alpha,beta);
vis[xx][yy]=false;
}
if(val<beta) beta=val;
if(alpha>beta) return beta;
}
if(opt) return beta;
}
if(step&1) return step-oo;
return oo-step;
}
int main(){
freopen("h.in","r",stdin);
freopen("h.out","w",stdout);
int x1,y1,x2,y2;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&mp[i][j]);
if(mp[i][j]==1) {x1=i,y1=j;}
else if(mp[i][j]==2) {x2=i,y2=j;}
vis[i][j]=mp[i][j];
}
int cx=maxmin(1,x1,y1,x2,y2,-inf,inf);
if(cx>0) printf("1 %d",oo-cx);
else printf("2 %d",cx+oo);
}
信息拦截
Alice和Bob经常利用网络传播一些不法信息。网络由n台电脑组成,Alice拥有1号电脑,Bob拥有n号电脑。有m对电脑间可以通过电缆单向传输信息。为了不被拦截,Alice每次传送信息都可以选取任意一条路径(经过的电脑可以重复),把信息沿着电缆传送给Bob。现在政府获知了这一情报,他们可以侵入恰好1台电脑,并获取经过这条电脑的所有信息。政府希望(无论Alice选择的路线如何)他们对Alice发送的每条信息都恰好获取1次(如果太少,会漏掉重要情报;如果太多,他们的电脑会被塞满的)。现在你需要求出政府可以选择入侵哪些电脑以达到要求。
对于100%的数据,2<=n<=500000,0<=m<=1000000,t<=10。
可能有自环
题解
分析一下题目要求:选取一个点满足 不属于一个点数>1的强联通分量切没有自环(使得信息不多),是1到n的路径的必经点(使得信息不漏)
那么就可以理出做题步骤:1.tarjan缩点 2.判必经点。
因为不知道有向图的割点能不能用tarjan求,所以就换了《算法竞赛》上的方法:对于有向无环图,求出fs[x]起点到x的路径条数,ft[x]终点到x的路径条数,根据乘法原理:如果fs[x]*ft[x]=fs[t]那么x就是s到t的必经点。
但是路径条数增长很快,所以考虑使用hash的思想,对路径条数取模,可能会误判。
求fs和ft可以用拓扑排序来做,但是考虑不能到达n的点对整个图是有影响的,所以建新图的时候只能把能到达n的点放进去。

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=500005;
const int maxm=1000005;
const int mod=1000000007;
int t,n,m;
int cnt,head[maxn];
int cur,low[maxn],dfn[maxn];
int top,num,s[maxn],res[maxn],mp[maxn];
bool in[maxn],flag[maxn],circle[maxn];
ll fs[maxn],ft[maxn];
struct edge{
int x,y,next;
}e[maxm],go[maxm],back[maxm];
int cntg,cntb,headg[maxn],headb[maxn];
int g[maxn],b[maxn];
template<class T>inline void read(T &x){
x=0;int f=0;char ch=getchar();
while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x = f ? -x : x ;
}
void add(int x,int y){
e[++cnt]=(edge){x,y,head[x]};
head[x]=cnt;
}
void addg(int x,int y){
go[++cntg]=(edge){x,y,headg[x]};
headg[x]=cntg;
}
void addb(int x,int y){
back[++cntb]=(edge){x,y,headb[x]};
headb[x]=cntb;
}
void init(){
cnt=num=cntg=cntb=cur=top=0;
memset(g,0,sizeof(g));
memset(b,0,sizeof(b));
memset(fs,0,sizeof(fs));
memset(ft,0,sizeof(ft));
memset(mp,0,sizeof(mp));
memset(res,0,sizeof(res));
memset(dfn,0,sizeof(dfn));
memset(head,0,sizeof(head));
memset(in,false,sizeof(in));
memset(headg,0,sizeof(headg));
memset(headb,0,sizeof(headb));
memset(circle,false,sizeof(circle));
}
void tarjan(int x){
if(x==n) in[x]=true;
dfn[x]=low[x]=++cur;
s[++top]=x;flag[x]=true;
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
if(!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(flag[y]) low[x]=min(low[x],dfn[y]);
in[x]|=in[y];
}
if(dfn[x]==low[x]){
num++;
int kk;
if(s[top]==x) mp[num]=x;
do{
kk=s[top--];
res[kk]=num;
flag[kk]=false;
}while(kk!=x);
}
}
void back_topsort(){
queue<int> q;
ft[res[n]]=1;
q.push(res[n]);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=headb[x];i;i=back[i].next){
int y=back[i].y;
b[y]--;
ft[y]+=ft[x];
if(ft[y]>=mod) ft[y]-=mod;
if(!b[y]) q.push(y);
}
}
}
void go_topsort(){
queue<int> q;
fs[res[1]]=1;
q.push(res[1]);
while(!q.empty()){
int x=q.front();
q.pop();
if(mp[x]&&!circle[mp[x]]&&fs[x]*ft[x]%mod==ft[res[1]]) s[++top]=mp[x];
for(int i=headg[x];i;i=go[i].next){
int y=go[i].y;
g[y]--;
fs[y]+=fs[x];
if(fs[y]>=mod) fs[y]-=mod;
if(!g[y]) q.push(y);
}
}
}
void solve(){
read(n);read(m);
init();
for(int i=1;i<=m;i++){
int x,y;
read(x);read(y);
if(x==y) circle[x]=true;
else add(x,y);
}
tarjan(1);
// for(int i=1;i<=n;i++) printf("%d ",res[i]);
// putchar(10);
if(!dfn[n]||res[1]==res[n]) {printf("0\n\n");return ;}//没联通
for(int x=1;x<=n;x++)
if(dfn[x]&&in[x]){
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
if(in[y]&&res[x]!=res[y]){
addg(res[x],res[y]);g[res[y]]++;
addb(res[y],res[x]);b[res[x]]++;
}
}
}
back_topsort();
go_topsort();
printf("%d\n",top);
for(int i=1;i<=top;i++)
printf("%d ",s[i]);
putchar(10);
}
int main(){
freopen("i.in","r",stdin);
freopen("i.out","w",stdout);
read(t);
while(t--) solve();
}
/*
4
5 5
1 3
4 5
3 2
2 4
4 2
*/
std是直接把新图建成无向图跑割点的。
