所以根据这个原则n^2建边即可,判一圈之后O(n)模拟即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(48+buf[buf[0]--]);
return ;
}
int tot,nxt[111111],goal[111111],first[111111],co[1111],a[1111],mini[1111],n,stack1[1111],stack2[1111],top1,top2,s,top;
bool tag;
void addedge(int a,int b){
++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;
++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;
return ;
}
void dfs(int tmpc,int pos){
if(co[pos]!=0){
if(co[pos]!=tmpc)
tag=true;
return ;
}
if(tag) return;
co[pos]=tmpc;
for(int p=first[pos];p;p=nxt[p])
dfs(3-tmpc,goal[p]);
return ;
}
signed main(){
n=read();
for(int i=1;i<=n;++i)
a[i]=read();
mini[n]=a[n];
for(int i=n-1;i;--i)
mini[i]=min(mini[i+1],a[i]);
for(int i=1;i<=n;++i)
for(int j=i+1;jmini[j+1])
addedge(i,j);
for(int i=1;i<=n;++i)
if(!co[i]) dfs(1,i);
if(tag){
putchar('0');
return 0;
}
s=1;top=1;
for(int i=1;i<=n*2;++i){
if(s<=n){
if(stack1[top1]==top){
--top1;
putchar('b');putchar(' ');
++top;
}else if(co[s]==1){
stack1[++top1]=a[s++];
putchar('a');putchar(' ');
}else if(stack2[top2]==top){
--top2;
putchar('d');putchar(' ');
++top;
}else{
stack2[++top2]=a[s++];
putchar('c');putchar(' ');
}
}else{
if(stack1[top1]==top){
--top1;
putchar('b');putchar(' ');
++top;
}else{
--top2;
putchar('d');putchar(' ');
++top;
}
}
}
return 0;
}
然后是同样简单的二分图匹配
(2)矩阵游戏(ZJOI2007)
题面依然见链接http://www.lydsy.com/JudgeOnline/problem.php?id=1059
分析:很显然,如果两点在同行或同列,那么无论如何变换其依然为同行或同列
我们现在只需要求是否有n点异行异列即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(48+buf[buf[0]--]);
return ;
}
int n,used[233],ans,con[233],T,map[233][233];
bool check(int x){
for(int i=1;i<=n;++i)
if(map[x][i]&&!used[i]){
used[i]=true;
if(con[i]==0||check(con[i])){
con[i]=x;
return true;
}
}
return false ;
}
void hungary(){
for(int i=1;i<=n;++i){
memset(used,0,sizeof(used));
if(check(i)) ++ans;
}
}
signed main(){
T=read();
while(T--){
memset(used,0,sizeof(used));
memset(con,0,sizeof(con));
ans=0;
n=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
map[i][j]=read();
hungary();
if(ans==n) puts("Yes");
else puts("No");
}
}
3.树剖系列
(1)水树剖求LCA
又一次贴代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline long long read(){
long long i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(48+buf[buf[0]--]);
return ;
}
#define stan 55555
int tot,nxt[stan*2],first[stan],goal[stan*2],dis[stan*2];
int sze[stan],to[stan],fa[stan],dep[stan],son[stan],top[stan];
int n,m,a,b,c;
void addedge(int a,int b,int c){
++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;dis[tot]=c;
++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;dis[tot]=c;
return ;
}
void preact(){
static int que[111111];
int q=1;
que[q]=0;dep[0]=1;
for(int i=1;i<=n;++i){
int u=que[i];sze[u]=1;
for(int p=first[u];p;p=nxt[p])
if(goal[p]!=fa[u]){
fa[goal[p]]=u;
dep[goal[p]]=dep[u]+1;
to[goal[p]]=to[u]+dis[p];
que[++q]=goal[p];
}
}
for(int i=n;i>=2;--i){
int u=que[i];
sze[fa[u]]+=sze[u];
if(sze[u]>sze[son[fa[u]]])
son[fa[u]]=u;
}
for(int i=1;i<=n;++i){
int u=que[i];
if(top[u]) continue;
for(int v=u;v;v=son[v])
top[v]=u;
}
return ;
}
int solve(int u,int v){
int x=u,y=v;
while(top[u]!=top[v]){
if(dep[top[u]]dep[v]) swap(u,v);
return to[x]+to[y]-to[u]*2;
}
signed main(){
n=read();
for(int i=1;i
(2)染色(SDOI2011)
题面其实很短,但我就是想甩链接http://www.lydsy.com/JudgeOnline/problem.php?id=2243
分析:其实是很裸的树链剖分,但是线段树合并时要注意
那么具体该注意些什么呢?
1.线段树标记下放后返回时要更改
lef[pos]=lef[pos<<1];righ[pos]=righ[pos<<1|1];
2.修改后合并时如果左右颜色相同则计数减一
if(lef[pos<<1|1]^righ[pos<<1]) cont[pos]=cont[pos<<1]+cont[pos<<1|1];
else cont[pos]=cont[pos<<1]+cont[pos<<1|1]-1;
3.查询时两个子区间如果左右颜色相同则返回值减一,但要确保能两区间都会有贡献
if(x<=mid&&mid
4.最后计数时要抛去算过一次的LCA
return pathsum(a,u)+pathsum(b,u)-1;
所以代码就调出来了
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline long long read(){
long long i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(48+buf[buf[0]--]);
return ;
}
#define stan 111111
int tot,nxt[stan*2],first[stan],goal[stan*2],dis[stan*2];
int sze[stan],to[stan],fa[stan],dep[stan],son[stan],top[stan],idx[stan],pos[stan],now;
int cont[stan*4],tag[stan*4],lef[stan*4],righ[stan*4];
int n,m,a,b,c,co[stan];
char order[10];
void addedge(int a,int b){
++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;
++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;
return ;
}
void preact(){
static int que[111111];
int q=1;
que[q]=1;dep[1]=1;
for(int i=1;i<=n;++i){
int u=que[i];sze[u]=1;
for(int p=first[u];p;p=nxt[p])
if(goal[p]!=fa[u]){
fa[goal[p]]=u;
dep[goal[p]]=dep[u]+1;
que[++q]=goal[p];
}
}
for(int i=n;i>=2;--i){
int u=que[i];
sze[fa[u]]+=sze[u];
if(sze[u]>sze[son[fa[u]]])
son[fa[u]]=u;
}
for(int i=1;i<=n;++i){
int u=que[i];
if(top[u]) continue;
for(int v=u;v;v=son[v]){
pos[v]=++now;
idx[now]=v;
top[v]=u;
}
}
return ;
}
void build(int x,int l,int r){
cont[x]=1;tag[x]=-1;
if(l==r) return ;
int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
return ;
}
void pushdown(int pos,int l,int r){
int tmp=tag[pos];tag[pos]=-1;
if(tmp==-1||l==r) return ;
cont[pos<<1]=cont[pos<<1|1]=1;
tag[pos<<1]=tag[pos<<1|1]=tmp;
lef[pos<<1]=lef[pos<<1|1]=tmp;
righ[pos<<1]=righ[pos<<1|1]=tmp;
return ;
}
void raiseup(int pos){
lef[pos]=lef[pos<<1];righ[pos]=righ[pos<<1|1];
if(lef[pos<<1|1]^righ[pos<<1]) cont[pos]=cont[pos<<1]+cont[pos<<1|1];
else cont[pos]=cont[pos<<1]+cont[pos<<1|1]-1;
return ;
}
void change(int pos,int l,int r,int x,int y,int d){
pushdown(pos,l,r);
if(x<=l&&r<=y){
lef[pos]=righ[pos]=d;
cont[pos]=1;tag[pos]=d;
return ;
}
int mid=l+r>>1;
if(x<=mid) change(pos<<1,l,mid,x,y,d);
if(y>mid) change(pos<<1|1,mid+1,r,x,y,d);
raiseup(pos);
}
int querysum(int pos,int l,int r,int x,int y){
pushdown(pos,l,r);
int ret=0;
if(x<=l&&r<=y) return cont[pos];
int mid=l+r>>1;
if(mid>=x) ret+=querysum(pos<<1,l,mid,x,y);
if(mid>1;
if(p<=mid) return check(pos<<1,l,mid,p);
else return check(pos<<1|1,mid+1,r,p);
}
int pathsum(int u,int v){
int ret=0;
while(top[u]!=top[v]){
ret+=querysum(1,1,n,pos[top[u]],pos[u]);
if(check(1,1,n,pos[top[u]])==check(1,1,n,pos[fa[top[u]]])) --ret;
u=fa[top[u]];
}
ret+=querysum(1,1,n,pos[v],pos[u]);
return ret;
}
void pathchange(int u,int v,int data){
while(top[u]!=top[v]){
change(1,1,n,pos[top[u]],pos[u],data);
u=fa[top[u]];
}
change(1,1,n,pos[v],pos[u],data);
}
int lca(int u,int v){
int x=u,y=v;
while(top[u]!=top[v]){
if(dep[top[u]]dep[v]) swap(u,v);
return u;
}
int solve(int a,int b){
int u=lca(a,b);
return pathsum(a,u)+pathsum(b,u)-1;
}
signed main(){
n=read();m=read();
for(int i=1;i<=n;++i)
co[i]=read();
for(int i=1;i
4.Floyd
略
5.2-SAT
奶牛议会(USACO2011.Jan)
甩链接才是最方便的...貌似是权限题
题面:n点m个限制,求对于一种合法方案每个点的取舍情况
分析:对于每个要求拆开正常建边即可,最后枚举每个点的取舍,判断一下即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(48+buf[buf[0]--]);
return ;
}
int tot,nxt[111111],goal[111111],first[4444],n,m,a,b,c,d,p,q;
char order[2222];
bool mark[2222];
void addedge(int a,int b){
++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;
return ;
}
void dfs(int pos){
mark[pos]=1;
for(int p=first[pos];p;p=nxt[p])
if(!mark[goal[p]])
dfs(goal[p]);
return ;
}
bool check(int pos){
memset(mark,0,sizeof(mark));
dfs(pos);
for(int i=1;i<=n;++i)
if(mark[2*i]&&mark[2*i-1])return 0;
return 1;
}
signed main(){
n=read();m=read();
for(int i=1;i<=m;++i){
a=read();
scanf("%s",order);
if(order[0]=='Y')
a=a*2-1;
else a<<=1;
if(a&1) b=a+1;
else b=a-1;
c=read();
scanf("%s",order);
if(order[0]=='Y')
c=c*2-1;
else c<<=1;
if(c&1) d=c+1;
else d=c-1;
addedge(b,c);addedge(d,a);
}
for(int i=1;i<=n;++i){
p=check(i*2-1);
q=check(i<<1);
if(!p&&!q){
puts("IMPOSSIBLE");
return 0;
}else if(p&&q) order[i]='?';
else if(!p) order[i]='N';
else order[i]='Y';
}
for(int i=1;i<=n;++i)
putchar(order[i]);
return 0;
}
6.最小生成树
版题,略
贴个代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0])putchar(buf[buf[0]--]+48);
return ;
}
struct Edge{
int a,b,l;
}edge[1111111];
int n,x,y,m,cnt,ans,fa[1111];
bool cmp(Edge x,Edge y){
return x.l
7.tarjan
这次我们用tarjan求割点
分析:对于一个点,如果其子节点无法不经过该点而到达其父节点部分,则该点为割点
很明显这是把它假装成一棵树,所以我们用了tarjan
注意第一个遍历的节点如果只有一个儿子的情况
还是甩个链接吧https://www.luogu.org/problem/show?pid=3388
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(48+buf[buf[0]--]);
return ;
}
int tot,nxt[222222],first[111111],goal[222222],dfn[111111],low[111111],ts,cutting[111111],ans,n,m,u,v;
void addedge(int a,int b){
++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;
++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;
return ;
}
void tarjan(int fa,int u){
dfn[u]=low[u]=++ts;
int son=0;
for(int p=first[u];p;p=nxt[p])
if(!dfn[goal[p]]){
++son;
tarjan(u,goal[p]);
low[u]=min(low[u],low[goal[p]]);
if(low[goal[p]]>=dfn[u]) cutting[u]=true;
}else if(dfn[goal[p]]
8.点分治
嗯就是我很久以前写过的那个
代码还是那个鬼样子
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline long long read(){
long long i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(48+buf[buf[0]--]);
return ;
}
int tot,n,k,nxt[22222],first[11111],goal[22222],dis[22222],cnt,a,b,c;
int sze[11111],maxsze[11111],step[11111],dist[11111],root,total;
bool vis[11111];
int nop,ans;
void addedge(int a,int b,int c){
++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;dis[tot]=c;
++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;dis[tot]=c;
return ;
}
void dfsroot(int pos,int fa){
sze[pos]=1;maxsze[pos]=0;
for(int p=first[pos];p;p=nxt[p])
if(goal[p]!=fa&&!vis[goal[p]]){
dfsroot(goal[p],pos);
sze[pos]+=sze[goal[p]];
if(sze[goal[p]]>maxsze[pos]) maxsze[pos]=sze[goal[p]];
}
if(total-sze[pos]>maxsze[pos]) maxsze[pos]=total-sze[pos];
if(maxsze[root]>maxsze[pos]) root=pos;
return ;
}
void topicaldfs(int pos,int fa){
++cnt;
step[cnt]=dist[pos];
for(int p=first[pos];p;p=nxt[p])
if(goal[p]!=fa&&!vis[goal[p]]){
dist[goal[p]]=dist[pos]+dis[p];
topicaldfs(goal[p],pos);
}
return ;
}
int calc(int pos,int dista){
int ret=0;
cnt=0,dist[pos]=dista;
topicaldfs(pos,0);
sort(step+1,step+cnt+1);
for(int l=1,r=cnt;l
9.模拟
生日礼物(SCOI2009)
08-09年生日三部曲之一
题面在此http://www.lydsy.com/JudgeOnline/problem.php?id=1293
维护两组堆都能过...
一个为起点,然后k次枚举讫点,完了
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(long long x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(48+buf[buf[0]--]);
return ;
}
priority_queue,greater >que[66];
int n,k,num[66],tmp1,tmp2,pos,ans;
bool tag;
signed main(){
ans=2147483647;
n=read();k=read();
for(int i=1;i<=k;++i){
num[i]=read();
for(int j=1;j<=num[i];++j){
pos=read();
que[0].push(pos);
que[i].push(pos);
}
}
while(!que[0].empty()){
tmp1=que[0].top();
tmp2=0;
for(int i=1;i<=k;++i){
while(que[i].top()
局部完结撒花