20190718

我是一如既往的弱。

T1. 星际旅行

很简单的一道乱搞题。

将所有的边拆成两条,问题变成去掉两条边,使得原图存在一条欧拉路
径。注意新图中所有点的度数均为偶数,只需按照去掉任意2个自环、去掉任
意1个自环和任意一条边、去掉两条有公共顶点的边进行讨论即可。注意图不连
通的判断方式,不是点不连通,而是边不连通。

标准题解说是要把无向边拆为两条有向边,那么问题就转化为去掉两条边使得原图存在一条欧拉路径。

要保证新图中所有点的度数都为偶数

我们先考虑两种简单情况

1. 删除任意两个自环 即 ans+=sum(be_back)*(sum(be_back)-1)/2;

2. 删除任意一个自环和一条边 即 ans+=m(处自环外的路径 双向边单向注意*2)*sum(back);

还有就是删除两条路径

我们考虑怎样删除可以使得所有点的度数为偶 显然是删除连接着同一个点的两条边

具体点呢?

删除如图所示的两条即可

用语言描述出来就是一条入边一条出边

对于一个点方案数为 C(入度,1)*(C(出度,1) -1)

记 入度或者出度为in 即in*(in-1) (式1)

为什么要减一呢 因为你不能同时把原来的一条完整的无向边完全砍掉

但是式1要除2 见 样例说明 (第七条路径与第一条不同)

然后可以快乐的提交了

题目很坑 要判边联通

值得一提的是可以选择并查集 效率很高

但是判断条件有点繁琐 一是存在两个大小>=2的集合 二是一个单独的点里有自环

数据还是很棒的

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define QAQ 100110
#define re register
#define ll long long
#define max(a,b) ( (a) > (b) ) ? (a) : (b)
#define min(a,b) ( (a) < (b) ) ? (a) : (b)
#define fup(i,a,b) for(re int i=a;i<=b;++i)
#define fdn(i,a,b) for(re int i=a;i>=b;--i)
int fa[QAQ],in[QAQ],fu[QAQ];
int n,m,be_back,sum;
bool judge[QAQ];
ll ans;
inline ll read(){
    re ll x(0),f(1);re char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}
int find(re int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void merge(re int x,re int y){
    x=find(x),y=find(y);
    fa[x]=y;
}
int main(){
    n=read(),m=read();
    fup(i,1,n)fa[i]=i;
    fup(i,1,m){
        re int x,y;
        x=read(),y=read();
        if(x==y){
            judge[x]=1;
            ++be_back;
        } else {
            merge(x,y);
            ++in[x];
            ++in[y];
            ++sum;
        }
    }
    fup(i,1,n)++fu[find(i)];
    bool flag=0;
    fup(i,1,n){
        if(fu[i]==1&&judge[i]){
            puts("0");
            return 0;
        }
        if(fu[i]>=2){
            if(!flag)flag=1;
            else {
                puts("0");
                return 0;
            }
        }
    }
    fup(i,1,n)ans+=1ll*(in[i]-1)*in[i]/2;
    ans+=1ll*be_back*(be_back-1)/2;
    ans+=1ll*be_back*sum;
    printf("%lld",ans);
}
SJQ

 


 

T2.砍树

不要二分!不要二分!!不要二分!!!

其实并不难,比如soul大神A掉了

问题等价于求一个最大的d,满足\

移项

然后把d除过去

(不会打 sigma 只能截题解QAQ)具体细节还是有点东西的

右边就变成了 C/d 向下取整

(联系数学函数 使 左边最大 小于 右边最小 则一定恒成立)

左右都是单调递减(非严格)

可以发现随着d的增大 左边的变化频率比较快 右边比较慢

 

(蓝色为右,红色为左)

为什么呢,其实很显然

左边相当与先有误差后把误差加和 右边只有一次误差 左边会把误差放大

可以手模几组例子

如 3/2-> 2 5/2->3 7/2->4

但 3/3-> 1 5/3->2 7/3->3

而右边 (3+5+7+8888888)== (3+5+7+8888888)/3

显然左边变化大

下面定义的区间指的是d的取值范围

对于每一个右边的区间 l->r 右边的值是不变的

而左边在疯狂变化

但是有单调性 即若式子在l可行那么整个 l->r 都可行

同时 r 处不可行则整个区间都不行

换言之,最优解在 r 处(如果中间交点处mid可行 那么r也可行 再换言之 若不行 则一定是在r出先开始)

即暴力验证 r 处的 d 大小是否合适

注意区间的范围就好了

 1 #include <vector>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 using namespace std;
 7 #define QAQ 100110
 8 #define re register
 9 #define ll long long
10 #define max(a,b) ( (a) > (b) ) ? (a) : (b)
11 #define min(a,b) ( (a) < (b) ) ? (a) : (b)
12 #define fup(i,a,b) for(re int i=a;i<=b;++i)
13 #define fdn(i,a,b) for(re int i=a;i>=b;--i)
14 inline ll read(){
15     re ll x(0),f(1);re char ch=getchar();
16     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
17     while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
18     return x*f;
19 }
20 int a[QAQ];
21 ll k,ans;
22 int n;
23 int main(){    
24     ll sum=0,minn=888888888,maxx=0;
25     n=read(),k=read();
26     fup(i,1,n)a[i]=read(),sum+=a[i];
27     sum+=k;
28     ll d=0;
29     while(1){
30         if(sum/(d+1)==0)break;
31         d=sum/(sum/(d+1));
32         re ll can=0;
33         fup(i,1,n){
34             ll tot=a[i]/d;
35             if(a[i]%d)++tot;
36             can+=tot*d;
37         }
38         if(can<=sum)ans=d;
39     }
40     printf("%lld",ans);
41 }
SJQ

 

T3.超级树

先%一手DeepinC Orz TQL!

题解太棒了!!!

主要还是如何想到这种鬼畜的状态数组定义

数言蔽之

1.分析维度 考虑转移可能性

2.分析时间,空间复杂度

3.注意各个元素间的“公平”(很难描述,但是各个数据间确实有着微妙的联系,在以后的矩阵乘,dp中会多解释)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #define Maxn 3050
 5 #define max(x,y) ((x)>(y)?(x):(y))
 6 #define Reg register
 7 using namespace std;
 8 long long sum,n,mod,dp[Maxn][Maxn];
 9 int main()
10 {
11     scanf("%lld%lld",&n,&mod);
12     dp[1][1]=dp[1][0]=1;
13     for(Reg int i=1;i<=n-1;++i)
14     {
15         for(Reg int j=0;j<=n-i+2;++j)
16         {
17             for(Reg int k=0;k<=n-i+2-j;++k)
18             {
19                 sum=(dp[i][j]*dp[i][k])%mod;
20                 dp[i+1][j+k]=(dp[i+1][j+k]+sum)%mod;
21                 //选j+k条路径 左和右分别选的贡献
22                 dp[i+1][j+k+1]=(dp[i+1][j+k+1]+sum)%mod;
23                 //选j+k+1条路径 左和右分别选+选根的贡献
24                 dp[i+1][j+k]=(dp[i+1][j+k]+sum*2*(j+k))%mod;
25                 //选j+k条路径 左(和右)分别选一条边(起点或终点)连到根的贡献
26                 if(j+k-1>=0) dp[i+1][j+k-1]=(dp[i+1][j+k-1]+sum*j*k*2)%mod;
27                 //选j+k-1条路径 左(右)选一条边到根在连右(左)的贡献
28                 if(j+k-1>=0) dp[i+1][j+k-1]=(dp[i+1][j+k-1]+sum*(j*j-j+k*k-k))%mod;
29                 //选j+k-1条路径 左(右)连根再连左(右)的贡献
30             }
31         }
32     }
33     printf("%lld",dp[n][1]%mod);
34     return 0;
35 }
SJQ

 

总体来说吧 我考的狗屁不是 不分析算法正确性(二分,最小生成树) 把题考虑太难(礼物,通讯,星际旅行)

心态也很爆炸 中间一段时间根本什么都没码

加油吧 lz爬也要爬到第一机房

Fighting!

posted @ 2019-07-18 20:41  No_Smily♥  阅读(195)  评论(0编辑  收藏  举报