bzoj1124 [POI2008]枪战Maf
Description
有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。
Input
输入n人数<1000000 每个人的aim
Output
你要求最后死亡数目的最小和最大可能
Sample Input
8
2 3 2 2 6 7 8 5
2 3 2 2 6 7 8 5
Sample Output
3 5
solution
图论题不是网络流之类的板子,一定是有性质的,否则不可做 --------某森林之王
性质:
整个图分为几个联通块,每个块一定是一个仙人球样子的东西
他的刺方向都向球连,每个球一定是 一个简单环或者是自杀的疯子
我们可以贪心
max:
无刺仙人球: n-1
有刺仙人球:n- 刺的端点个数
自杀球:n-刺的端点个数
min:
从 刺的端点(不死)now开始
shoot[now]一定要打死,这样会给后面的疯子更多的生存机会
shoot[shoot[now]]如果indgree==1(没有枝杈)塞入队列
这样把树链贪心完,接近仙人球时
1.当shoot[shoot[now]]==仙人球与刺相接,接点没有必要死,不会进仙人球
2.但是当shoot[now]为接点时,就会进树,进行仙人球内部的贪心
正是由于这个,仙人球一定会被分成几段,******而刺进入球遍历的一段一定不用再遍历*****
3.最后再把刺没遍历完的球遍历即可
(这个题还有wq的清奇dp思路,但我一下午 被干烧脑了.....)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<queue> 5 #define mem(a,b) memset(a,b,sizeof(a)) 6 using namespace std; 7 const int N=1000006; 8 const int INF=1000500; 9 inline int minn(int a,int b){return a<b?a:b;} 10 inline int maxn(int a,int b){return a>b?a:b;} 11 struct son 12 { 13 int v,next; 14 }; 15 son a1[N<<1]; 16 int first[N<<1],e; 17 void addbian(int u,int v) 18 { 19 a1[e].v=v; 20 a1[e].next=first[u]; 21 first[u]=e++; 22 } 23 /*void addyuan(int u,int v) 24 { 25 ayuan[eyuan].v=v; 26 ayuan[eyuan].next=firstyuan[u]; 27 firstyuan[u]=eyuan++; 28 }*/ 29 30 int n,v[N],fanv[N],maxl,minl; 31 int now,low[N],dfn[N],sum,num[N],dui[N]; 32 int zhan[N*4],he,flag[N]; 33 int indyuan[N],indtan[N]; 34 int mx,mn,ifzisha[N]; 35 36 inline void tan(int x) 37 { 38 low[x]=dfn[x]=++now; 39 zhan[++he]=x;flag[x]=1; 40 41 if(dfn[v[x]]==-1) 42 { 43 tan(v[x]); 44 low[x]=minn(low[v[x]],low[x]); 45 } 46 else 47 if(flag[v[x]]) 48 low[x]=minn(low[x],dfn[v[x]]); 49 50 if(low[x]==dfn[x]) 51 { 52 ++sum; 53 while(1) 54 { 55 int temp=zhan[he--]; 56 flag[temp]=0; 57 dui[temp]=sum; 58 ++num[sum]; 59 if(temp==x)break; 60 } 61 } 62 } 63 64 void finmax() 65 { 66 int insum0=0,dian=0; 67 for(int i=1;i<=n;++i) 68 if(v[i]==i) 69 ifzisha[dui[i]]=1; 70 for(int i=1;i<=sum;++i) 71 { 72 if(num[i]==1) 73 { 74 if(ifzisha[i]) 75 ++mx; 76 else 77 { 78 ++dian; 79 if(indtan[i]==0) 80 ++insum0; 81 } 82 } 83 else 84 { 85 if(indtan[i]==0) 86 mx+=(num[i]-1); 87 else 88 mx+=num[i]; 89 } 90 } 91 //printf("insum0=%d dian=%d\n",insum0,dian); 92 mx+=(dian-insum0); 93 } 94 95 queue<int> q; 96 int vis[N]; 97 98 void finmin() 99 { 100 for(int i=1;i<=n;++i) 101 { 102 if(v[i]==i) 103 {++mn;vis[i]=1;} 104 else 105 if(!indyuan[i]) 106 {q.push(i);vis[i]=1;} 107 } 108 //printf("mn=%d\n",mn); 109 while(!q.empty()) 110 { 111 int now=q.front();q.pop(); 112 //printf("now=%d\n",now); 113 if(vis[v[now]])continue; 114 vis[v[now]]=1;++mn; 115 --indyuan[v[v[now]]]; 116 if(indyuan[v[v[now]]]==0&&!vis[v[v[now]]]) 117 { 118 vis[v[v[now]]]=1; 119 q.push(v[v[now]]); 120 } 121 }//遍历树链,同时又可以遍历仙人球的环 122 //cout<<0; 123 //printf("mn=%d\n",mn); 124 for(int i=1;i<=n;++i) 125 { 126 if(vis[i])continue; 127 vis[i]=1;int now=1; 128 for(int j=v[i];;j=v[j]) 129 { 130 if(vis[j])break; 131 ++now;vis[j]=1; 132 } 133 mn+=(now+1)/2; 134 } 135 } 136 137 int main(){ 138 //freopen("maf3.in","r",stdin); 139 mem(dfn,-1); 140 mem(first,-1); 141 scanf("%d",&n); 142 for(int i=1;i<=n;++i) 143 { 144 scanf("%d",&v[i]); 145 ++indyuan[v[i]]; 146 addbian(v[i],i); 147 } 148 for(int i=1;i<=n;++i) 149 if(dfn[i]==-1) 150 tan(i); 151 152 /*printf("\n"); 153 for(int i=1;i<=n;++i) 154 printf("%d ",dui[i]); 155 printf("\n");*/ 156 157 for(int i=1;i<=n;++i) 158 if(dui[i]!=dui[v[i]]) 159 ++indtan[dui[v[i]]]; 160 161 /*printf("indtan=\n"); 162 for(int i=1;i<=n;++i) 163 printf("%d ",indtan[i]); 164 printf("\n");*/ 165 166 finmax(); 167 finmin(); 168 printf("%d %d",mn,mx); 169 while(1); 170 return 0; 171 }