C.http://codeforces.com/contest/557/problem/C
题意:有一个有n条腿的桌子,每根桌腿有一个l表示该桌腿的长度,还有一个d表示去掉该桌腿所需要的费用。现在要去掉一些桌腿使得整个桌子稳定下来,桌子稳定的条件是当前存在的桌腿超过半数是当前剩余桌腿的最长长度,先求需要花费的最小费用
分析:首先对桌腿按照长度从小到大进行排序并进行离散化,将长度相同的桌腿保存在一起。然后我们逐一枚举最大长度,对于所有超过最大长度的桌腿一定都要去掉,而对于所有长度小于最大长度的桌腿,我们需要使剩下的桌腿数量小于当前这个最大长度桌腿的数量,将那些花费高的桌腿优先去除。我们可以设置一个sum[i]数组来维护所有桌腿长度小于等于i的花费之和。那么我们何如处理从长度小于最大长度中去除花费大的?因为考虑到桌腿的花费最大只有200,我们将最大长度从小到大处理,当每次处理完一个长度,都更新used[i]数组,used[i]表示的含义是花费为i的桌腿有多少,从小到大处理就能保证之前所存的长度都是小于当前最大长度的。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=1e5+10; 6 const int maxm=200+10; 7 const int inf=1e9; 8 struct node{ 9 int l,d,id; 10 }arr[maxn]; 11 int num[maxn],num_[maxn],sum[maxn],used[maxm]; 12 13 bool cmp(node a,node b) 14 { 15 return a.l<b.l; 16 } 17 18 int main() 19 { 20 int n,m,i,j,k,x,y,z,cnt,ans,now; 21 while ( scanf("%d",&n)!=EOF ) { 22 for ( i=1;i<=n;i++ ) scanf("%d",&arr[i].l); 23 for ( i=1;i<=n;i++ ) scanf("%d",&arr[i].d); 24 sort(arr+1,arr+1+n,cmp); 25 cnt=0; 26 memset(sum,0,sizeof(sum)); 27 memset(num,0,sizeof(num)); 28 for ( i=1;i<=n;i++ ) { 29 if ( i!=1 ) { 30 if ( arr[i].l>arr[i-1].l ) arr[i].id=++cnt; 31 else arr[i].id=cnt; 32 } 33 else arr[i].id=++cnt; 34 num[arr[i].id]++; 35 sum[arr[i].id]+=arr[i].d; 36 } 37 num_[0]=0; 38 for ( i=1;i<=n;i++ ) { 39 num_[i]=num_[i-1]+num[i]; 40 sum[i]+=sum[i-1]; 41 } 42 ans=inf; 43 memset(used,0,sizeof(used)); 44 for ( i=1;i<=cnt;i++ ) { 45 now=sum[cnt]-sum[i]; 46 x=num[i]-1; 47 if ( num_[i-1]>x ) { 48 x=num_[i-1]-x; 49 for ( j=1;j<=200;j++ ) { 50 if ( used[j]<=x ) { 51 now+=used[j]*j; 52 x-=used[j]; 53 } 54 else { 55 now+=x*j; 56 x=0; 57 break; 58 } 59 if ( x==0 ) break; 60 } 61 } 62 ans=min(ans,now); 63 for ( j=num_[i-1]+1;j<=num_[i];j++ ) used[arr[j].d]++; 64 } 65 printf("%d\n",ans); 66 } 67 return 0; 68 }
D.http://codeforces.com/contest/557/problem/D
题意:给出一个无向图,有n个点,m条边,先要求最少添加几条边可以使图中出现奇圈,同时还需要访问数
分析:判断奇圈->判断是否为二分图->染色法。
需要添加的边一定是0,1,2,3这四种情况中的一种
需要特判:
当m==0时,代表整个无向图都是孤立的点,这时候想要有奇圈,至少添加三条边,此时答案为C(3,n);
当整个无向图不是二分图时,则原图一定存在奇圈,则答案为1
其他情况:
需要添加两条边:当没有入度/出度>2的点,即每个点最多只与一条边相连,这时候如果想要有奇圈,则需要至少连两条边,答案为m*(n-2),代表的含义是m条边上的两点每次只需要在剩下的n-2个点中取一个点分别于该边的两端相连即可组成奇圈。
需要添加一条边:对于剩下情况,我们最多只需要添一条边即可形成奇圈。具体做法是在一个已经染色好了部分(每个连通分支),将颜色相同的两点连接起来即可形成奇圈,对于该连通分支答案为C(2,w)+C(2,b) (w代表在这个连通分支中白色的数量,b代表在这个连通分支中黑色的数量)。最后对所有连通分支的答案求和即可
注意:无向图建双向边用vis判重即可,不要建从小到大的单向边
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 using namespace std; 6 typedef long long ll; 7 const int maxn=1e5+10; 8 ll num[maxn],cnt1[maxn],cnt2[maxn],color[maxn],pos[maxn]; 9 bool vis[maxn]; 10 ll n; 11 vector<ll>G[maxn]; 12 13 bool dfs(ll u,ll id) 14 { 15 if ( !vis[u] ) num[id]++; 16 pos[u]=id; 17 vis[u]=true; 18 for ( ll i=0;i<G[u].size();i++ ) { 19 ll v=G[u][i]; 20 if ( color[u]==color[v] ) return false; 21 if ( !color[v] ) { 22 color[v]=3-color[u]; 23 if ( !dfs(v,id) ) return false; 24 } 25 } 26 return true; 27 } 28 29 int main() 30 { 31 ll m,i,j,k,x,y,z,u,v,ans,cnt; 32 bool flag; 33 while ( scanf("%lld%lld",&n,&m)!=EOF ) { 34 for ( i=1;i<=n;i++ ) { 35 G[i].clear(); 36 cnt1[i]=cnt2[i]=0; 37 num[i]=color[i]=0; 38 vis[i]=false; 39 } 40 for ( i=1;i<=m;i++ ) { 41 scanf("%lld%lld",&u,&v); 42 G[u].push_back(v); 43 G[v].push_back(u); 44 } 45 if ( m==0 ) { 46 ans=n*(n-1)*(n-2)/6; 47 printf("3 %lld\n",ans); 48 continue; 49 } 50 flag=false; 51 cnt=0; 52 for ( i=1;i<=n;i++ ) { 53 if ( vis[i] ) continue; 54 color[i]=1; 55 if ( !dfs(i,++cnt) ) { 56 flag=true; 57 break; 58 } 59 } 60 if ( flag ) { 61 printf("0 1\n"); 62 continue; 63 } 64 for ( i=1;i<=n;i++ ) { 65 if ( color[i]==1 ) { 66 cnt1[pos[i]]++; 67 } 68 else { 69 cnt2[pos[i]]++; 70 } 71 } 72 ans=0; 73 for ( i=1;i<=cnt;i++ ) { 74 if ( num[i]<=2 ) continue; 75 ans+=(cnt1[i]-1)*cnt1[i]/2+(cnt2[i]-1)*cnt2[i]/2; 76 } 77 if ( ans==0 ) { 78 ans=(n-2)*m; 79 printf("2 %lld\n",ans); 80 } 81 else printf("1 %lld\n",ans); 82 } 83 return 0; 84 }