A
http://codeforces.com/problemset/problem/229/A
题意:给100个长1e4的01串,问最少的移动次数,造出一列全是1的
做法:我是把所有1找出来,如果有一行没有1显然gg。然后因为循环,所以在前面和后面各补一个。然后枚举1列的位置,对每行二分取到最小值求和即可。
代码:
#include <bits/stdc++.h>
using namespace std;
char str[105][10500];
int cnt[105];
int rec[105][10500];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++)scanf("%s", str[i]+1);
bool ok=true;
for(int i=1;i<=n;i++){
bool flag=false;
for(int j=1;j<=m;j++)if(str[i][j]=='1')flag=true;
if(!flag){
ok=false;
break;
}
}
if(!ok){
printf("-1\n");
return 0;
}
memset(cnt, 0, sizeof(cnt));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)if(str[i][j]=='1')rec[i][++cnt[i]]=j;
rec[i][0]=rec[i][cnt[i]]-m;
rec[i][cnt[i]+1]=rec[i][1]+m;
}
int M=105*10500;
/* for(int i=1;i<=n;i++){
for(int j=0;j<=cnt[i]+1;j++)printf("%d ", rec[i][j]);
printf("\n");
}*/
for(int i=1;i<=m;i++){
int tmp=0;
for(int j=1;j<=n;j++){
tmp+=min(i-*(upper_bound(rec[j], rec[j]+cnt[j]+2, i)-1), *(lower_bound(rec[j], rec[j]+cnt[j]+1, i))-i);
}
M=min(M, tmp);
}
printf("%d\n", M);
}
B
http://codeforces.com/problemset/problem/229/B
题意:简单的最短路,附加条件是每个点有些时间不能走。
做法:在dij里改一下就行了。我一开始改了T了,后来发现因为会一直重复访问不能走的一段,加了一个d1数组防止重复访问就过了。
代码:
#include <bits/stdc++.h>
using namespace std;
struct edge{
int to, cost;
};
typedef pair<int, int> P;
int n;
vector<edge>g[105000];
int d[105000];
int d1[105000];
vector<int>rec[105000];
void dij(int s){
priority_queue<P, vector<P>, greater<P> >que;
for(int i=1;i<=n;i++)d[i]=d1[i]=1e9+1e8;
int st=0;
for(vector<int>::iterator it=rec[s].begin();it!=rec[s].end();it++){
if(*it<st)continue;
else if(*it==st)st++;
else break;
}
d[s]=d1[s]=st;
que.push(P(st, s));
while(!que.empty()){
P p=que.top();que.pop();
int v=p.second;
if(d[v]<p.first)continue;
for(vector<edge>::iterator it=g[v].begin();it!=g[v].end();it++){
edge e=*it;
if(d1[e.to]>d[v]+e.cost){
d1[e.to]=d[e.to]=d[v]+e.cost;
if(e.to==n)break;
for(vector<int>::iterator it1=rec[e.to].begin();it1!=rec[e.to].end();it1++){
if(*it1<d[e.to])continue;
else if(*it1==d[e.to])d[e.to]++;
else break;
}
que.push(P(d[e.to], e.to));
}
}
}
}
int main()
{
int m;
scanf("%d%d", &n, &m);
for(int i=1;i<=m;i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
edge t1, t2;
t1.to=b, t1.cost=c;
g[a].push_back(t1);
t2.to=a, t2.cost=c;
g[b].push_back(t2);
}
for(int i=1;i<=n;i++){
int num;
scanf("%d", &num);
for(int j=1;j<=num;j++)
{
int tmp;
scanf("%d", &tmp);
rec[i].push_back(tmp);
}
}
dij(1);
if(d[n]==1e9+1e8)printf("-1\n");
else printf("%d\n", d[n]);
}
C
http://codeforces.com/contest/229/problem/C
题意:将一个1e6个点的完全图取1e6条边的子图,求这个子图和它的反图里一共有多少个三角形。
做法:考虑完全图的三角形数,是C(n,3);对于集合A、B,C(n,3)=由同1个集合的边构成的三角形+由A、B共同构成的三角形。所以我们只需要用C(n,3)减去三边分属于A、B集合的三角形即可。在一个n完全图中,一次拿掉一条边,与这条边有关的三角形有n-2个,减去n-2。但如果这条边的两端点已经有边,则减去n-2就多减了,作为补偿,要加上这两个点已有的边数,因为这些边和当前边确定的三角形已经在之前被减过了、
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rec[1050000];
int main()
{
memset(rec, 0, sizeof(rec));
ll n, m;
scanf("%I64d%I64d", &n, &m);
if(n<=2){
printf("0\n");
return 0;
}
ll ans=n*(n-1)*(n-2)/6;
memset(rec, 0, sizeof(rec));
for(int i=1;i<=m;i++){
int x, y;
scanf("%d%d", &x, &y);
ans-=(n-2-rec[x]-rec[y]);
rec[x]++;
rec[y]++;
}
printf("%I64d\n", ans);
}
因为po主太菜了,D和E不会,待赛后填坑吧。
E
http://codeforces.com/contest/229/problem/E
题意:略
做法:看了xhr大爷的题解后发现,我特么题少看了个条件就是同名的价格两两不同,之后的事情就是正常的概率dp了。注意dp存的是前i种物品取了j个边界值的所有情况的成功率之和,所以最后还要除掉所有情况的种类数。
代码:
#include <bits/stdc++.h>
using namespace std;
int n, m;
int pre[1050];
int rec[1050][1050];
int tot;
int cnt[1050];
int upp[1050];
bool exs[1050];
int tag;
double c[1050][1050];
double dp[1050][1050];
int main()
{
scanf("%d%d", &n, &m);
tot=0;
for(int i=1;i<=m;i++){
scanf("%d", &cnt[i]);
for(int j=1;j<=cnt[i];j++){
scanf("%d", &rec[i][j]);
pre[++tot]=rec[i][j];
}
}
sort(pre+1, pre+1+tot);
tag=pre[tot-n+1];
memset(exs, false, sizeof(exs));
memset(upp, 0, sizeof(upp));
for(int i=1;i<=m;i++){
for(int j=1;j<=cnt[i];j++){
if(rec[i][j]==tag)exs[i]=true;
else if(rec[i][j]>tag)upp[i]++;
}
}
for(int i=1;i<=1005;i++)
for(int j=0;j<=i;j++){
if(j==0||j==i)c[i][j]=1;
else c[i][j]=c[i-1][j-1]+c[i-1][j];
}
int eql=0;
int supp=0;
for(int i=1;i<=m;i++)if(exs[i])eql++;
for(int i=1;i<=m;i++)supp+=upp[i];
for(int i=0;i<=1005;i++)for(int j=0;j<=1005;j++)dp[i][j]=0;
dp[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<=n-supp;j++)
{
if(exs[i]&&j){
dp[i][j]=dp[i-1][j]/c[cnt[i]][upp[i]]+dp[i-1][j-1]/c[cnt[i]][upp[i]+1];
}
else {
dp[i][j]=dp[i-1][j]/c[cnt[i]][upp[i]];
}
}
printf("%.11f\n", dp[m][n-supp]/c[eql][n-supp]);
}