ACM训练日记—4月17日

原创 2018年04月17日 21:56:53

        这两天除了打比赛就是在看组合数学的资料博客。感觉组合数学的题目还是对于我来说挺难的。

先整理部分比赛题,还没完全看懂全部题目。比较菜QAQ

然后整理到今天看的一道启发比较大的组合数学题。(组合问题,容斥原理,高斯消元)

埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛

 F题:1 + 2 = 3?

题目:小Y在研究数字的时候,发现了一个神奇的等式方程,他屈指算了一下有很多正整数x满足这个等式,比如1和2,现在问题来了,他想知道从小到大第N个满足这个等式的正整数,请你用程序帮他计算一下。

     推规律可以发现就是求第i个二进制数1和1之间不相邻的数,可以推出其中关于斐波那契数列的规律。

其实这个题既可以找规律也可以数位dp,关于数位dp的我还得补。

下面是一位大佬的代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
#define mod 2000000000
LL mi(int n)//快速幂
{
    LL a = 1;
    LL b = 2;
    while(n)
    {
        if(n % 2)
            a *= b;
        b *= b;
        n >>= 1;
    }
    return a;
}
int main()
{
    int i, j, k;
    LL m, n;
    LL a[65];
    a[0] = 1;
    a[1] = 2;
    a[2] = 1;
    a[3] = 2;
    for(i = 4; i <= 64; i++)
    {
        a[i] = a[i - 1] + a[i - 2];
    }
    for(i = 2; i <= 64; i++)
    {
        a[i] += a[i - 1];
    }
    int T;
    scanf("%d", &T);
    while(T--)
    {
        LL ans = 0;
        cin >> n;
        n++;
        while(n)
        {
            if(n == 1)
            {
                break;
            }
            if(n == 2)
            {
                ans++;
                break;
            }
            for(i = 1; i < 64; i++)//相当于逐位的求
            {
                if(n <= a[i])
                {
                    n -= a[i - 1];
                    ans += mi(i - 1);
                    break;
                }
            }
        }
        cout << ans << endl;
    }
    return 0;
}

  D—数字游戏

小埃和小森在玩一个数字游戏,小埃先从区间[L1, R1]里选择1个数字n1,小森看到小埃选的数字后,从[L2,R2]里选择1个数字n2, 将n1和n2连接在一起(n1在前, n2在后),形成一个新的数字,若这个数字可以被mod整除,那么小森获胜,否则小埃获胜。若两个人均采取最优策略,试问谁获胜?

       官方题解是

首先 n1 只要考虑%mod 有哪些余数就可以了,这个复杂度是 O(mod)。 而对于 n2,你需要分长度考虑有哪些余数,这个复杂度是 O(2*mod),对于存在同长度一段 大于 mod 的情况下,直接输出 LOSE 即可(这个剪枝比较重要)。 然后就可以枚举 n1 的余数,枚举 n2 的长度来判断是否可能 WIN。O(9*mod)

int main()

{
    int t,a,b,c,d,e;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
        if(d-c+1>=e)
            printf("LOSE\n");
        else
            printf("WIN\n");
    }
    return 0;
}

       HDU—5852  Intersection is not allowed!

下面来自:https://blog.csdn.net/v5zsq/article/details/52419187

Description 
一个n*n棋盘,第一行第a1,a2,…,ak列有k个棋子,每个棋子都只能往下和往右走,每个棋子的终点是第n行第b1,b2,…,bk列,问这k个棋子到达各种的终点且路径不交叉的方法数 
Input 
第一行一整数T表示用例组数,每组用例首先输入两整数n和k表示棋盘规模和棋子数量,之后k个整数ai表示k个棋子的起点,最后k个整数bi表示k个棋子的终点(1<=n<=10^5,1<=k<=100,1<=a1 < a2 < … < ak <=n,1<=b1 < b2 < … < bk <=n) 
Output 
对于每组用例,输出合法方案数,结果模1e9+7 
Sample Input 

5 2 
1 2 
3 4 
Sample Output 
50 
Solution 
首先考虑两个棋子的情况,即一个棋子从a1到b1,另一个棋子从a2到b2,两条路径不交叉的方案数,首先不考虑交叉方案数显然是C(b1-a1+n-1,n-1)*C(b2-a2+n-1,n-1),对于一个a1->b1,a2->b2且路径交叉的方案,如果我们把最下面一个交叉点之后的两条路径交换那么就对应了一个a1->b2,a2->b1的方案;对于一个a1->b2,a2->b1的方案,显然这两条路径必然会相交,那么我们把最后一个交叉点之后的两条路径交换就又对应一个a1->b1,a2->b2的路径交叉的方案,故我们建立了a1->b1,a2->b2交叉路径与a1->b2,a2->b1的方案的一一对应,那么不合法方案数就是C(b2-a1+n-1,n-1)*C(b1-a2+n-1,n-1) 
对于多个棋子的情况,由容斥原理,假设某些棋子路径发生了交叉,那么我们采用两个棋子的分析方法,把这些交叉的路径从最后下面一个交叉点之后交换,那么就变成了一个1~n序列的重排,我们不妨设其为c序列,表示第i个棋子从(1,ai)出发到(n,ci),那么这个排列对答案的贡献就取决于c序列的逆序对数,逆序对数为奇则做负贡献,为偶则做正贡献,那么就有 
这里写图片描述 
故问题转化为求一个n阶方阵行列式,用高斯消元O(n^3)即可解决 

代码来自另一位大佬:https://blog.csdn.net/y1196645376/article/details/52226296

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define eps 1e-6
typedef long long ll;
const ll mod = 1e9+7;
const int N = 2e5+10;
ll inv[N] = {1,1};  //inv[i] i的逆元。
ll fac[N] = {1,1};    // fac[i] i!%mod
ll facv[N] = {1,1};   // facv[i] i!的逆元
void init()
{
    for(int i = 2 ; i < N ; i ++)
        inv[i] = (mod-mod/i) * inv[mod%i] % mod;
    for(int i = 2 ; i < N ; i ++)
        facv[i] = facv[i-1] * inv[i] % mod;
    for(int i = 2 ; i < N ; i ++)
        fac[i] = fac[i-1]*i % mod;
}
ll C(ll n,ll m)
{
    if(m < 0) return 0;
    return fac[n]*facv[m]%mod*facv[n-m]%mod;
}
int s[110],e[110];
ll a[110][110];
ll det(int n)
{
    ll ans=1;
    int sign = 0;
    for(int i=0;i<n;i++)//当前行
    {
        for(int j=i+1;j<n;j++)//当前之后的每一行,因为每一行的当前第一个数要转化成0(想想线性代数中行列式的计算)
        {
            int x=i,y=j;
            while(a[y][i])//利用gcd的方法,不停地进行辗转相除
            {
                ll t=a[x][i]/a[y][i];
                for(int k=i;k<n;k++)
                    a[x][k]=(a[x][k]-a[y][k]*t)%mod;
                swap(x,y);
            }
            if(x!=i)//奇数次交换,则D=-D'整行交换
            {
                for(int k=0;k<n;k++)
                    swap(a[i][k],a[x][k]);
                sign^=1;
            }
        }
        if(a[i][i]==0)//斜对角中有一个0,则结果为0
        {
            return 0;
        }
        else
            ans=ans*a[i][i]%mod;
    }
    if(sign!=0)
        ans*=-1;
    if(ans<0)
        ans+=mod;
    return ans;
}
int main()
{
    int n,k,t;
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        for(int i = 0 ; i < k ; i ++)
            scanf("%d",&s[i]);
        for(int i = 0 ; i < k ; i ++)
            scanf("%d",&e[i]);
        for(int i = 0 ; i < k ; i ++)
        {
            for(int j = 0 ; j < k ; j ++)
            {
                a[i][j] = C(n-1+e[j]-s[i],e[j]-s[i]);
            }
        }
        printf("%lld\n",det(k));
    }
    return 0;
}

         这道题利用行列式的性质计算容斥原理的方法我还得回去好好研究研究,还得回去翻翻高代课本了。

2017年2月17日实习日记

qemu 运行虚拟机挂在gpu之后,需要拥有自己的ip地址进行x11vnc链接。 坑了一天。 参考资料 ------------------------ 访问qemu虚拟机 http://bl...
  • CoderHattonLiu
  • CoderHattonLiu
  • 2017-02-18 00:01:45
  • 386

2018.1.17日记

通过代码的描述,知道类的真正意义就是在描述事物。属性和功能统称为事物中的成员。 事物的成员分为两种:成员属性和成员功能。 成员属性在代码中的体现就是成员变量 成员功能在代码中的体现就是成员方法 ...
  • Dongser
  • Dongser
  • 2018-01-25 17:24:26
  • 34

2018年1月23日训练日记

昨晚去补了EOJ月赛的三道题,第一题石头剪刀布不给样例比较难受,不过理清楚思路或者列举一下情况就很容易做了。第二题求妙,刚开始没注意最大数字出现多次的情况,一直WA,后来知道改一下就过了。。。还是细节...
  • LSD20164388
  • LSD20164388
  • 2018-01-23 17:37:03
  • 176

2018年2月2日训练日记

今晚两场比赛,希望不要再掉分了啊... 图论-网络流:目前看了80来道题,也感觉懂了不少东西,然而有的题目还是不会建模。。。还是看的少了吧。 好题:UVA 11082 Matrix Decompr...
  • LSD20164388
  • LSD20164388
  • 2018-02-03 09:27:28
  • 151

4月5日6日毕设日记

鉴于项目的基本功能已经实现,所以下一步是逐渐完善功能和界面。 1 界面主要想采用《AndroidSDK开发实战演练》第六章讲的几个标签版面的那个案例,一个主页xml,一个结果xml,一个图片xml;...
  • SmartDoubleXiao
  • SmartDoubleXiao
  • 2015-04-06 21:44:08
  • 506

4月15日到17日学习心得二

17.4随机数  注意计算机程序中给出的都是伪随机数。c++中不再像C一样使用rand()而是引擎+分布。default_random_engine e; for(size_t i=00;i&amp;...
  • xr5827
  • xr5827
  • 2018-04-19 17:10:59
  • 8

2018年4月4日训练日记

4月2日-4月4日这段时间主要还是做题,但是明显的感觉出用到的一些知识点有些忘了,从set、vector到强连通、拓扑排序、最小生成树到数位树形状压dp,二分图最大匹配、网络流到树剖、LCA、RMQ。...
  • LSD20164388
  • LSD20164388
  • 2018-04-05 10:01:07
  • 40

ACM训练日记—12月17日

最近打的两场cf真郁闷,上了2分,下了2分。实在是太菜了,一直只能做出来3道水题。有基本功不扎实的原因,写道水题费时间太长了,读题目的速度有待提高,第四题往往都是来不及做就结束了。 下面整理一下这两...
  • snayf
  • snayf
  • 2017-12-17 21:55:58
  • 48

2010年4月17日星期六

  • linstein
  • linstein
  • 2010-04-19 18:42:00
  • 176

4月17日 训练

#include #include #include #include #include #include #include #include using namespace std;...
  • SaoJieNanHai
  • SaoJieNanHai
  • 2014-04-17 23:31:23
  • 125
收藏助手
不良信息举报
您举报文章:ACM训练日记—4月17日
举报原因:
原因补充:

(最多只允许输入30个字)