【20161030la 】总结

就写个题解

1、

生成树(Tree)

有一种图形叫做五角形圈。一个五角形圈的中心有1个由n个顶点和n条边组成的圈。在中心的这个n边圈的每一条边同时也是某一个五角形的一条边,一共有n个不同的五角形。这些五角形只在五角形圈的中心的圈上有公共的顶点。如图0所示是一个4-五角形圈。

现在给定一个n五角形圈,你的任务就是求出n五角形圈的不同生成树的数目。还记得什么是图的生成树吗?一个图的生成树是保留原图的所有顶点以及顶点的数目减去一这么多条边,从而生成的一棵树。

注意:在给定的n五角形圈中所有顶点均视为不同的顶点。

数据范围

50% n<=10;100% n<=100;

我当时推的式子还是挺复杂的,是n^2的,不过很明显这是一个特殊的图,需要特殊考虑。一个生成树是原图减掉n+1条边。

我一开始的式子:

ans[i]=sigma(c[i][j]*4^(i-j)*4*j) 就是枚举中间的环割多少条边,C是组合数。

正解是知道一定有且只有一个五边形割了两条边,而且其中一条是中心环里面的边,就枚举这个,就是

ans[i]=i*4*5^(i-1)

 

2、

 复杂的大门(gate)(0.5s,256MB)

Description

你去找某bm玩,到了门口才发现要打开他家的大门不是一件容易的事……
他家的大门外有n个站台,用1n的正整数编号。你需要对每个站台访问一定次数以后大门才能开启。站台之间有m个单向的传送门,通过传送门到达另一个站台不需要花费任何代价。而如果不通过传送门,你就需要乘坐公共汽车,并花费1单位的钱。值得庆幸的是,任意两个站台之间都有公共汽车直达。
现在给你每个站台必须访问的次数Fi,对于站台i,你必须恰好访问Fi次(不能超过)。
我们用uvw三个参数描述一个传送门,表示从站台u到站台v有一个最多可以使用w次的传送门(不一定要使用w次)。值得注意的是,对于任意一对传送门(u1,v1)(u2,v2),如果有u1<u2,则有v1≤v2;如果有v1<v2,则有u1≤u2;且u1=u2v1=v2不同时成立。
你可以从任意的站台开始,从任意的站台结束。出发去开始的站台需要花费1单位的钱。你需要求出打开大门最少需要花费多少单位的钱。

HINT

20%的数据满足n≤10m≤50;对于所有的wFi,满足1≤w,Fi≤10。有50%的数据满足n≤1000m≤10000100%的数据满足1≤n≤100001≤m≤100000;对于所有的uv,满足1≤u,v≤nu≠v;对于所有的wFi,满足1≤w,Fi≤50000。以上的每类数据中都存在50%的数据满足对于所有的wFi,有w=Fi=1

正常人就往网络流方向想,就我这样的傻逼贪心。。。

并且一直觉得这样的贪心是对的,事实上正解也是贪心,但不是这种啊ORZ。。

然而我的傻逼贪心是可以过的,数据ORZ。。

每个点入度和出度固定,想到拆点弄成二分图,然后用最少的钱就是走最多的传送门,跑最大流。

但是数据很大,但是图有个特殊的性质,就是上面加粗部分,就知道这个图是不相交的,就可以从上往下贪心(能流多少流多少)

当然打出来毫无网络流的样子。。

放代码:

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define Maxm 100010
 8 #define Maxn 10010
 9 
10 int a[Maxn],b[Maxn],d[Maxn],sum[Maxn];
11 
12 struct node
13 {
14     int x,y,c;
15 }t[Maxm*2];
16 
17 int mymin(int x,int y) {return x<y?x:y;}
18 
19 bool cmp(node x,node y) {return (x.x==y.x)?(x.y<y.y):(x.x<y.x);}
20 
21 int main()
22 {
23     int n,m;
24     scanf("%d%d",&n,&m);
25     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
26     for(int i=1;i<=n;i++) b[i]=a[i],d[i]=a[i];
27     memset(sum,0,sizeof(sum));
28     int sm=0;
29     for(int i=1;i<=m;i++)
30     {
31         scanf("%d%d%d",&t[i].x,&t[i].y,&t[i].c);
32     }
33     sort(t+1,t+1+m,cmp);
34     for(int i=1;i<=m;i++)
35     {
36         int x=t[i].x,y=t[i].y,c=t[i].c;
37         int now=mymin(mymin(b[x],d[y]),c);
38         sm+=now;
39         b[x]-=now;d[y]-=now;
40     }
41     int ans=0;
42     for(int i=1;i<=n;i++)
43     {
44         ans+=a[i];
45     }
46     ans-=sm;
47     printf("%d\n",ans);
48     return 0;
49 }
--

每天智障24小时

 

3、

%%%%%GDXB

反正我是想一辈子想不到。。

那个啥,偶数次,好难搞。

其实可以想成弄成二进制(当然是一个很大的二进制,只是这样想)异或和为0。

假设我们要分成i段,为了满足异或和为0,知道前i-1段的异或和就知道i是啥了。

但是也有不满足的情况,就是求出来的i表示的数为0或者和前面某个数相等,减去这些情况就行了。

一开始先算排列,再除以m!就好了。

f[i]=p[2^n-1][i]-f[i-1]-f[i-2]*(i-1)*(2^n-i+1)

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define Mod 100000007
 8 #define LL long long
 9 #define Maxn 1000010
10 
11 LL p[Maxn],tp[Maxn],f[Maxn];
12 
13 LL qpow(LL x,LL b)
14 {
15     LL ans=1;
16     while(b)
17     {
18         if(b&1) ans=(ans*x)%Mod;
19         x=(x*x)%Mod;
20         b>>=1;
21     }
22     return ans;
23 }
24 
25 int main()
26 {
27     LL n,m;
28     scanf("%lld%lld",&n,&m);
29     
30     tp[0]=1;
31     for(LL i=1;i<=n;i++) tp[i]=(tp[i-1]*2)%Mod;
32     p[0]=1;
33     for(LL i=1;i<=m;i++) p[i]=(p[i-1]*(tp[n]+Mod-i))%Mod;
34     
35     f[0]=1;f[1]=0;
36     for(LL i=2;i<=m;i++)
37     {
38         f[i]=p[i-1]-f[i-1];f[i]=(f[i]%Mod+Mod)%Mod;
39         
40         
41         LL x=tp[n]-i+1;x=(x%Mod+Mod)%Mod;
42         
43         f[i]=(f[i]-((f[i-2]*(i-1))%Mod)*x )%Mod;
44         f[i]=(f[i]%Mod+Mod)%Mod;
45     }
46     
47     LL bm=1;
48     for(LL i=1;i<=m;i++) bm=(bm*i)%Mod;
49     bm=qpow(bm,Mod-2);
50     bm=(bm*f[m])%Mod;
51     printf("%lld\n",bm);
52     return 0;
53 }
%%%%

 

4、

Description

在一些一对一游戏的比赛(如下棋、乒乓球和羽毛球的单打)中,我们经常会遇到A胜过BB胜过CC又胜过A的有趣情况,不妨形象的称之为剪刀石头布情况。有的时候,无聊的人们会津津乐道于统计有多少这样的剪刀石头布情况发生,即有多少对无序三元组(A, B, C),满足其中的一个人在比赛中赢了另一个人,另一个人赢了第三个人而第三个人又胜过了第一个人。注意这里无序的意思是说三元组中元素的顺序并不重要,将(A, B, C)(A, C, B)(B, A, C)(B, C, A)(C, A, B)(C, B, A)视为相同的情况。

N个人参加一场这样的游戏的比赛,赛程规定任意两个人之间都要进行一场比赛:这样总共有场比赛。比赛已经进行了一部分,我们想知道在极端情况下,比赛结束后最多会发生多少剪刀石头布情况。即给出已经发生的比赛结果,而你可以任意安排剩下的比赛的结果,以得到尽量多的剪刀石头布情况。

HINT

30%的数据中,N≤ 6
100%的数据中,N≤ 100

 这题建模好像看起来挺简单。

但是没什么事都不想费用流的我啊,(总觉得他很慢)

一开始一直想最小割ORZ

如果不符合剪刀石头布,一定是3个人中有一个人赢了两场。

计算每个人的出度(表示赢的场数) ans=总的-sigma(c[k[i]][2]) k表示i这个人的出度。

额,就是把每个两两之间的比赛弄成一个点,再连向那两个人。[如果还没有比的话]

因为总费用是(k-1)*k/2 这个不是普通的加,就要拆边。

跑最小费用最大流。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define Maxn 110
#define Maxm 41000
#define INF 0xfffffff

int a[Maxn][Maxn],v[Maxn];

struct node
{
    int x,y,f,c,next,o;
}t[Maxm*2];int len;
int first[Maxm];

void ins(int x,int y,int f,int c,bool p)
{
    t[++len].x=x;t[len].y=y;t[len].f=f;t[len].c=c;
    t[len].next=first[x];first[x]=len;
    if(p)
    {
        t[len].o=len+1;
        t[++len].x=y;t[len].y=x;t[len].f=0;t[len].c=-c;
        t[len].next=first[y];first[y]=len;t[len].o=len-1;
    }
    else t[len].o=0;
}

int mymin(int x,int y) {return x<y?x:y;}

int st,ed,cnt;
bool inq[Maxm];
int pre[Maxm],flow[Maxm],dis[Maxm];

queue<int > q;
bool ffind()
{
    while(!q.empty()) q.pop();
    
    for(int i=1;i<=ed;i++) dis[i]=INF,pre[i]=-1,inq[i]=0;
    
    dis[st]=0;q.push(st);inq[st]=1;
    flow[st]=INF;
    while(!q.empty())
    {
        int x=q.front();
        for(int i=first[x];i;i=t[i].next) if(t[i].f>0)
        {
            int y=t[i].y;
            if(dis[y]>dis[x]+t[i].c)
            {
                dis[y]=dis[x]+t[i].c;
                pre[y]=i;
                flow[y]=mymin(flow[x],t[i].f);
                if(!inq[y])
                {
                    inq[y]=1;
                    q.push(y);
                }
            }
        }
        q.pop();inq[x]=0;
    }
    if(pre[ed]==-1) return 0;
    return 1;
}

void output()
{
    for(int i=1;i<=len;i+=2)
    {
        printf("%d->%d %d %d %d\n",t[i].x,t[i].y,t[i].f,t[i].c,t[i].next);
    }
    printf("\n");
}

int max_flow()
{
    int sum=0;
    while(ffind())
    {
        int now=ed;
        sum+=flow[ed]*dis[ed];
        while(now!=st)
        {
            t[pre[now]].f-=flow[ed];
            t[t[pre[now]].o].f+=flow[ed];
            now=t[pre[now]].x;
        }
    }
    return sum;
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
        scanf("%d",&a[i][j]);
    len=0;
    memset(first,0,sizeof(first));
    cnt=n;
    st=n+n*(n-1)/2+1;ed=st+1;
    memset(v,0,sizeof(v));
    for(int i=1;i<=n;i++)
     for(int j=i+1;j<=n;j++)
     {
           cnt++;
          if(a[i][j]==1) v[i]++;
          else if(a[i][j]==0) v[j]++;
          else {ins(st,cnt,1,0,0);ins(cnt,i,1,0,1);ins(cnt,j,1,0,1);}
     }

    for(int i=1;i<=n;i++)
     for(int j=v[i];j<n-1;j++)
        ins(i,ed,1,j,1);
    
    t[0].f=INF;
    int now=max_flow();
    for(int i=1;i<=n;i++) now+=v[i]*(v[i]-1)/2;
    printf("%d\n",n*(n-1)*(n-2)/6-now);
    return 0;
}
--

 

2016-10-31 11:17:40

posted @ 2016-10-31 11:13  konjak魔芋  阅读(262)  评论(0编辑  收藏  举报