[POJ2942 Knights of the Round Table]【点的双连通分量】

【题目大意】
给一个无向图,对于每个点,求他是否在一个奇环中。如果不是ans++。输出ans。
【算法分析】
先求点的双连通分量,然后对于每个双连通分量,如果他里面有奇环,那么整个双连通分量上的点都在某奇环上。(这个可以用可以用奇偶性证明,实际上就是把整个图搞成两个有重叠部分的环)
然后判断一个双连通分量里有无奇环就可以交替染色。如果出现一条边的两端颜色相等,那么有奇环。
否则无奇环(利用反证法证明)
【其它】
原来我一直用的那个自创的沙茶并查集类Tarjan思想的算的是边的双连通分量,而不是点的双连通分量。于是果断WA到沙茶。。。哎,算法问题。。。
然后终于下定决心学习正版的Tarjan。学习结束重写1Y。
【CODE】
#include #include #include #define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define clr(T) memset(T,0,sizeof(T))
const int N=1005;
struct edge{int x,y,next;}g[N*N];
int n,m,times,tot,e,Qt,cnt,check;
int a[N][N],ls[N],vis[N];
int DFN[N],done[N],Stack[N*N],low[N],Q[N*N],color[N*N],odd[N];

inline void addedge(int x,int y){
g[++e].x=x; g[e].y=y; g[e].next=ls[x]; ls[x]=e;
color[e]=0;
}

void init(){
int i,j,x,y;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
a[i][j]=i!=j;
for (i=1;i<=m;i++){
scanf("%d%d",&x,&y);
a[x][y]=a[y][x]=0;
}
times=tot=cnt=0;
e=1;
clr(ls);
clr(vis);
clr(DFN);
clr(done);
clr(low);
clr(odd);
for (i=1;i for (j=i+1;j<=n;j++)
if (a[i][j]){
addedge(i,j);
addedge(j,i);
}
}

void draw(int x){
vis[x]=cnt;
for (int t=ls[x];t;t=g[t].next)
if (color[t]==cnt && vis[g[t].y]!=cnt){
odd[g[t].y]=1-odd[x];
draw(g[t].y);
}else
if (color[t]==cnt && vis[g[t].y]==cnt && odd[g[t].y]==odd[x])
check=1;
}

void solve(int x){
cnt++;
for (int i=1;i<=Qt;i++){
color[Q[i]]=cnt;
color[Q[i]^1]=cnt;
}
check=0;
odd[x]=1;
draw(x);
if (check)
for (int i=1;i<=Qt;i++)
done[g[Q[i]].x]=done[g[Q[i]].y]=1;
}

void dfs(int x,int fa){
DFN[x]=low[x]=++times;
for (int t=ls[x];t;t=g[t].next){
int y=g[t].y;
if (y==fa) continue;
if (!DFN[y]){
Stack[++tot]=t;
dfs(y,x);
low[x]=min(low[x],low[y]);
if (DFN[x]<=low[y]){
Qt=0;
while (Stack[tot]!=t)
Q[++Qt]=Stack[tot–];
Q[++Qt]=Stack[tot–];
solve(x);
}
}else if (DFN[y] Stack[++tot]=t;
low[x]=min(low[x],DFN[y]);
}
}
}

void Tarjan(){
for (int i=1;i<=n;i++)
if (!DFN[i])
dfs(i,0);
}

int main(){
for (;;){
scanf("%d%d",&n,&m);
if (!n) break;
init();
Tarjan();
int ans=0;
for (int i=1;i<=n;i++)
if (!done[i]) ans++;
printf("%dn",ans);
}
return 0;
}

[Croatian Olympiad in Informatics 【kolo】]【找规律】【正模拟】【逆模拟】

【题目链接】
http://www.hsin.hr/coci/
提交地址:http://evaluator.hsin.hr/
【题目大意】
1..n的n个数站成一个圈。
一共进行k轮
对于第i轮
选取位于最顶端的数。
他和自己逆时针方向相邻的那个数交换,一直交换pl[i]次。 其中pl[i]为第i个质数。
输入n,k,A。
A表示一开始的第A个数。
问进行了k轮以后,A的两个邻居是谁?
【算法分析】
假如n<=10^5,那么还可以用平衡树模拟搞一下。
但是n超大,所以不能如此。
质数自然是用筛法筛出来了。
然后我们可以先搞出A最后到了哪里。然后算出他的邻居,最后再逆回来,看一开始在哪里,就可以知道编号了。
然后具体的,容易发现,假如经过n*(n-1)次交换,那么相当于没动。
所以每次处理可以讲交换次数先%(n*(n-1))。  这里小心会爆int。
然后我们可以发现每转n次,那么将相当于忽略了顶端的那个数以后,都向顺时针转了一格。
于是首先是模拟转了p/n次以后的。
最后模拟剩下的转圈。这个不容易想。

然后逆回去将相当于反过来做了。。。
【其它】第一次交,95分。原因是我%(n*n)而不是%(n*(n-1))。。。居然这都有这么高分。。。泪流满面。
【Record】
est # Score Time Memory Result 1 5 0.52s 10392 kB Correct! 2 5 0.52s 10392 kB Correct! 3 5 0.52s 10392 kB Correct! 4 5 0.52s 10392 kB Correct! 5 5 0.52s 10392 kB Correct! 6 5 0.52s 10392 kB Correct! 7 5 0.53s 10392 kB Correct! 8 5 0.54s 10392 kB Correct! 9 5 0.54s 10392 kB Correct! 10 5 0.54s 10392 kB Correct! 11 5 0.53s 10392 kB Correct! 12 5 0.53s 10392 kB Correct! 13 5 0.54s 10392 kB Correct! 14 5 0.54s 10392 kB Correct! 15 5 0.54s 10392 kB Correct! 16 5 0.60s 10392 kB Correct! 17 5 0.66s 10392 kB Correct! 18 5 0.68s 10392 kB Correct! 19 5 0.67s 10392 kB Correct! 20 5 0.66s 10392 kB Correct! 【CODE】
#include #include #include #include using namespace std;
const int Size=7368789;
typedef long long lld;
int n,k,A,pt;
int pl[500005];
bool v[Size];

int Get(int x){return (x%n+n)%n;}

void Next(int &x,int p){
p=(int)(p%((lld)(n)*(n-1)));
if (x==0){
x=Get(p);
return;
}
x-=p/n;
if (x<=0) x--;
x=Get(x);
if (p%n>=x) x=Get(x-1);
}

void Pre(int &x,int p){
p=(int)(p%((lld)(n)*(n-1)));
if (p%n==x){
x=0;
return;
}
if (x x+=p/n;
if (x>=n) x=Get(x+1);
}

void Sai(){
memset(v,true,sizeof(v));
int i,j;
for (i=2;i for (j=i+i;j pt=0;
for (i=2;pt if (v[i]) pl[pt++]=i;
}

int main(){
int i,ans1,ans2;
// freopen("kolo.in.7","r",stdin);
// freopen("kolo.out","w",stdout);
cin >> n >> k >> A;
A–;
Sai();
for (i=0;i ans1=Get(A+1);
ans2=Get(A-1);
for (i=k-1;i>=0;i–){
Pre(ans1,pl[i]);
Pre(ans2,pl[i]);
}
cout << ans1+1 << " " << ans2+1 << endl;
}

[COCI Croatian Olympiad in Informatics 【hrastovi】]【二分】【各种排序】

【题目链接】http://www.hsin.hr/coci/
【题目大意】
给定N个点和Q个矩形。
对于每个矩形,求有多少个点落在其边界上。
【算法分析】
把一个矩形分成4条线段。然后分别处理。
可以先处理x=k的线段。
就是,把点按x为第一关键字,y为第二关键字排序。
然后对于每个x=k的线段,然后二分出对应范围,弄个Sum数组搞一下。就可以了。
对于y=k的线段,类似地处理。
就可以很快地搞定。
【其它】1Y。
【CODE】
#include #include #include struct Point{int x,y;}p[300005];
struct Ques{int x1,y1,x2,y2;}Q[100005];
struct Line{int key,l,r,pos;}L[200005];
int n,Qt,Lt;
int Sum[300005],ans[100005];

void init(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d%d",&p[i].x,&p[i].y);
scanf("%d",&Qt);
for (int i=1;i<=Qt;i++)
scanf("%d%d%d%d",&Q[i].x1,&Q[i].y1,&Q[i].x2,&Q[i].y2);
}

int cmpx_p(const void *A,const void *B){
if ( ((Point*)A)->x != ((Point*)B)->x ) return ((Point*)A)->x-((Point*)B)->x;
return ((Point*)A)->y-((Point*)B)->y;
}

int cmp_L(const void *A,const void *B){
return ((Line*)A)->key-((Line*)B)->key;
}

int Findx_l(int st,int ed,int y){
int l=st,r=ed,mid;
while (l mid=(l+r)/2;
if (y<=p[mid].y) r=mid;
else l=mid+1;
}
if (y<=p[l].y) return l;
return 0x7FFFFFFF;
}

int Findx_r(int st,int ed,int y){
int l=st,r=ed,mid;
while (l mid=(l+r+1)/2;
if (p[mid].y<=y) l=mid;
else r=mid-1;
}
if (p[l].y<=y) return l;
return -0x7FFFFFFF;
}

void solvex(){
int i,j,stp,edp,stL,edL,l,r;
Lt=0;
for (i=1;i<=Qt;i++){
L[++Lt].l=Q[i].y1; L[Lt].r=Q[i].y2; L[Lt].pos=i; L[Lt].key=Q[i].x1;
L[++Lt].l=Q[i].y1; L[Lt].r=Q[i].y2; L[Lt].pos=i; L[Lt].key=Q[i].x2;
}
qsort(p+1,n,sizeof(Point),cmpx_p);
qsort(L+1,Lt,sizeof(Line),cmp_L);
edL=1;
for (i=1;i<=n;i=edp+1){
stp=i;
for (edp=i;edp for (stL=edL;stL<=Lt && L[stL].key for (edL=stL;edL if (stL>Lt || L[stL].key!=p[i].x) continue;
for (j=stp;j<=edp;j++) Sum[j]=j-stp+1;
for (j=stL;j<=edL;j++){
l=Findx_l(stp,edp,L[j].l);
r=Findx_r(stp,edp,L[j].r);
if (l>r) continue;
ans[L[j].pos]+=Sum[r]-Sum[l]+1;
}
}
}

int cmpy_p(const void *A,const void *B){
if ( ((Point*)A)->y != ((Point*)B)->y ) return ((Point*)A)->y-((Point*)B)->y;
return ((Point*)A)->x-((Point*)B)->x;
}

int Findy_l(int st,int ed,int x){
int l=st,r=ed,mid;
while (l mid=(l+r)/2;
if (x<=p[mid].x) r=mid;
else l=mid+1;
}
if (x<=p[l].x) return l;
return 0x7FFFFFFF;
}

int Findy_r(int st,int ed,int x){
int l=st,r=ed,mid;
while (l mid=(l+r+1)/2;
if (p[mid].x<=x) l=mid;
else r=mid-1;
}
if (p[l].x<=x) return l;
return -0x7FFFFFFF;
}

void solvey(){
int i,j,stp,edp,stL,edL,l,r;
Lt=0;
for (i=1;i<=Qt;i++){
L[++Lt].l=Q[i].x1+1; L[Lt].r=Q[i].x2-1; L[Lt].pos=i; L[Lt].key=Q[i].y1;
if (L[Lt].l>L[Lt].r) Lt–;
L[++Lt].l=Q[i].x1+1; L[Lt].r=Q[i].x2-1; L[Lt].pos=i; L[Lt].key=Q[i].y2;
if (L[Lt].l>L[Lt].r) Lt–;
}
qsort(p+1,n,sizeof(Point),cmpy_p);
qsort(L+1,Lt,sizeof(Line),cmp_L);
edL=1;
for (i=1;i<=n;i=edp+1){
stp=i;
for (edp=i;edp for (stL=edL;stL<=Lt && L[stL].key for (edL=stL;edL if (stL>Lt || L[stL].key!=p[i].y) continue;
for (j=stp;j<=edp;j++) Sum[j]=j-stp+1;
for (j=stL;j<=edL;j++){
l=Findy_l(stp,edp,L[j].l);
r=Findy_r(stp,edp,L[j].r);
if (l>r) continue;
ans[L[j].pos]+=Sum[r]-Sum[l]+1;
}
}
}

int main(){
// freopen("HRASTOVI.in","r",stdin);
// freopen("HRASTOVI.out","w",stdout);
init();
solvex();
solvey();
for (int i=1;i<=Qt;i++) printf("%dn",ans[i]);
return 0;
}

[JSOI2008]最小生成树计数 【分组思想】【暴力并差集】【DFS】

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1016
【算法分析】
假设我们使用的是kruskal算法,那么就很容易弄出来了。
首先先把边按权排序,然后等权的分成同一组。
易知如果是最小生成树的话,那么在经历过前K组以后。
无论前面是怎么搞的,连通性都应该是一致的~
那么就可以分段搞。
而且每一组里互不干涉。
最后利用分步乘法原理就可以得解。
至于每一步怎么求,那么就需要注意到题目的一个猥琐的小角落有这么一句话:注意:具有相同权值的边不会超过10条。
然后由于这个条件,于是就可以暴力DFS求每一步。最终得出解。
【其它】
1Y。另外那个并差集有点小假。。。回溯的时候,我直接弄个数组Copy过去保存,最终依然是15MSAC。
另外要注意可能会有没有生成树的情况。
【CODE】
#include #include #include #define Copy(a,b) memcpy(a+1,b+1,sizeof(int)*n)
const int mod=31011;
struct edge{int x,y,w;}g[1005];
int n,m,ans;
int f[105],tf[105];
int cmp(const void *A,const void *B){return ((edge*)A)->w-((edge*)B)->w;}

void init(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d%d",&g[i].x,&g[i].y,&g[i].w);
}

int gf(int k){
if (f[k]==k) return k;
return f[k]=gf(f[k]);
}

int tgf(int k){
if (tf[k]==k) return k;
return tf[k]=tgf(tf[k]);
}

int tmpf[25][105],cnt,dep,done;
void dfs(int i,int ed,int total){
if (done==total){cnt++; return;}
if (i>ed) return;
if (tgf(g[i].x)!=tgf(g[i].y)){
Copy(tmpf[dep],tf);
tf[tf[g[i].x]]=tf[g[i].y];
dep++;
done++;
dfs(i+1,ed,total);
dep–;
done–;
Copy(tf,tmpf[dep]);
}
dep++;
dfs(i+1,ed,total);
dep–;
}

void solve(){
int i,j,k,p=n,total;
ans=1;
for (i=1;i<=n;i++) f[i]=tf[i]=i;
for (i=1;i<=m;i=j+1){
for (j=i;j total=0;
for (k=i;k<=j;k++)
if (gf(g[k].x)!=gf(g[k].y)){
f[f[g[k].x]]=f[g[k].y];
total++;
}
p-=total;
cnt=dep=done=0;
dfs(i,j,total);
Copy(tf,f);
ans=ans*cnt%mod;
}
if (p!=1) printf("0n");
else printf("%dn",ans);
}

int main(){
init();
qsort(g+1,m,sizeof(edge),cmp);
solve();
return 0;
}

[BeiJing2010组队]能量魔方 Cube 【神来一割】★★

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1976

【算法分析】

这题是膜拜了解题报告才会得。

我只想说。。。我被虐到泪流满面

然后我来复述BT的解题报告。

首先,如果用bfs黑白交替染色,会发现每个点只会有一种颜色。(实际上就是到左上角那个点的哈密顿距离的奇偶性)

然后我们把奇点放左边,偶点放右边。

我们再定义:只有正能量的水晶能放出能量,而放出的能量就是它附近的负水晶个数。

这样。我们就可以利用一个最小割模型来求解。

我们想假设所有的点都是P点,并且他能放出最多的能量。(就是他放出和他相邻的格子数的能量)

然后就开始BT了。。。

定义:对于一个割,

如果奇点被割到和源相连的那一边。那么算是P,否则算是N。

如果偶点被割到和汇相连的那一边。那么算是P,否则算是N。

然后对应地。

我们连边S->奇点,容量为该奇点相邻点的个数。

连边偶点->T,容量为该偶点相邻点的个数。(其实除了边界,都是6)

然后对于相邻的奇偶两点,连边 奇点->偶点,容量为2。

这样连的理由是。

如果S->奇点的边成为割边,那么该奇点就变成N了。那么将不再放出能量。所以减去属于他放出的能量。

如果偶点->T的边成为割边,那么该偶点就变成N了。那么也不再放出能量。所以减去属于他放出的能量。

如果奇点->偶点的边成为割边,那么必然(奇点属于S集)(偶点属于T集) 【否则怎么叫割边。。。】

那么就是这个奇点和那个偶点都同时成为了P。那么他们放出的能量都要减1。一共减2。

原因是:本来假设相邻的点都是N,所以当成可以放出全部能量,但是因为相邻的这个不是N。所以大家放出的能量都要减~

所以最后的答案就是:每个点的相邻格子数之和减去最小割。

【其它】

感叹一句:太imba了。

我写的sap再次过不了。。。难道又疵了。。。

改成dinic才过。。。

另外强力膜拜75MS的tracyhenry大神。

【CODE】

#include

#include

#include #include using namespace std;
const int N=40;
const int INF=1000000000;
int n,Sum;
int pos[N][N][N];
char map[N][N][N];
bool odd[N][N][N];
struct edge{int x,y,c;edge *next,*op;};
struct Network_t{
       edge g[1000000],*ls[100000],*cur[100000];
       int vs,vt,d[100000],Flow,Q[100000],e;
      
       void addedge(int x,int y,int c){
            g[++e].x=x; g[e].y=y; g[e].c=c; g[e].next=ls[x]; ls[x]=&g[e]; g[e].op=&g[e+1];
            g[++e].x=y; g[e].y=x; g[e].c=0; g[e].next=ls[y]; ls[y]=&g[e]; g[e].op=&g[e-1];
       }
      
       int dfs(int p,int nf){
           if (p==vt) return nf;
           int tmp,FF=0;
           for (edge *&t=cur[p];t;t=t->next)
             if (t->c && d[t->x]+1==d[t->y] && (tmp=dfs(t->y,min(nf,t->c)))){
               FF+=tmp;
               t->c-=tmp; t->op->c+=tmp;
               if (!(nf-=tmp)) break;
             }
           if (!cur[p]) d[p]=INF;
           return FF;
       }
      
       bool bfs(){
            for (int i=vs;i<=vt;i++){
                d[i]=INF;
                cur[i]=ls[i];
            }
            d[vs]=1; Q[1]=vs;
            for (int st=1,ed=1;d[Q[st]]              for (edge *t=ls[Q[st]];t;t=t->next)
                if (t->c && d[t->y]==INF) d[ Q[++ed]=t->y ]=d[t->x]+1;
            if (d[vt]==INF) return false;
            return true;
       }
      
       void dinic(){
            Flow=0;
            int tmp;
            while (bfs())
              while (tmp=dfs(vs,INF)) Flow+=tmp;
       }
}Network;

bool flag(int x,int y,int z){return (0<=x && xvoid init(){
     scanf("%d",&n);
     int i,j,k,cnt=0;
     char ch;
     for (k=0;k       for (i=0;i         for (j=0;j             ch=getchar();
             while (ch==’ ‘ || ch==’n’) ch=getchar();
             map[k][i][j]=ch;
             pos[k][i][j]=++cnt;
             if (!i && !j) odd[k][i][j]=k%2; else
             if (!i) odd[k][i][j]=!odd[k][i][j-1]; else
             odd[k][i][j]=!odd[k][i-1][j];
         }
     Network.vs=0; Network.vt=++cnt;
     for (k=0;k       for (i=0;i         for (j=0;j             cnt=0;
             if (flag(k-1,i,j)) cnt++;
             if (flag(k+1,i,j)) cnt++;
             if (flag(k,i-1,j)) cnt++;
             if (flag(k,i+1,j)) cnt++;
             if (flag(k,i,j-1)) cnt++;
             if (flag(k,i,j+1)) cnt++;
             Sum+=cnt;
             if (odd[k][i][j]){
               Network.addedge(Network.vs,pos[k][i][j],cnt);
               switch (map[k][i][j]){
                 case ‘P’:Network.addedge(Network.vs,pos[k][i][j],INF); break;
                 case ‘N’:Network.addedge(pos[k][i][j],Network.vt,INF); break;
               }
               if (flag(k-1,i,j)) Network.addedge(pos[k][i][j],pos[k-1][i][j],2);
               if (flag(k+1,i,j)) Network.addedge(pos[k][i][j],pos[k+1][i][j],2);
               if (flag(k,i-1,j)) Network.addedge(pos[k][i][j],pos[k][i-1][j],2);
               if (flag(k,i+1,j)) Network.addedge(pos[k][i][j],pos[k][i+1][j],2);
               if (flag(k,i,j-1)) Network.addedge(pos[k][i][j],pos[k][i][j-1],2);
               if (flag(k,i,j+1)) Network.addedge(pos[k][i][j],pos[k][i][j+1],2);
             }else{
               Network.addedge(pos[k][i][j],Network.vt,cnt);
               switch (map[k][i][j]){
                 case ‘P’:Network.addedge(pos[k][i][j],Network.vt,INF); break;
                 case ‘N’:Network.addedge(Network.vs,pos[k][i][j],INF); break;
               }
             }
         }
}

int main(){
    init();
    Network.dinic();
    printf("%dn",Sum-Network.Flow);
    return 0;
}