zoukankan      html  css  js  c++  java
  • 2013ACM多校联合(2)_HUT 解题报告

     

     感谢其它高校的负责人,严格的审题等工作~~~。

     预祝大家本场训练赛玩的高兴。。。

     训练赛 RankList

     

    解题报告转摘自  Lyush   

     

    A.简单的想法题。使用hash表或者是map存储所有数,然后从最小的数开始找从这个数开始的连续P倍数的个数X,那么需要删除的数的个数为X/2

    View Code
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cstdlib>
    #include <map>
    #include <cassert>
    #include <ctime>
    using namespace std;
    
    int N, K;
    map<long long,bool>mp;
    
    int main() {
        map<long long,bool>::iterator it;
        int sum;
        while (scanf("%d %d", &N, &K) == 2) {
            sum = 0;
            mp.clear();
            int c;
            for (int i = 0; i < N; ++i) {
                scanf("%d", &c);
                mp[c] = true;
            }
            int cnt;
            long long t;
            for (it = mp.begin(); it != mp.end(); ++it) {
                cnt = 0;
                long long t = it->first;
                while (mp[t]) {
                    mp[t] = false;
                    t = K * t;
                    ++cnt;
                }
                sum += cnt / 2;
            }
            printf("%d\n", N - sum);
        }
        return 0;
    }

     

    B.组合数学题,通过经典的一一对应原则推导出答案为N^(N-2)http://wenku.baidu.com/view/2e1ab2757fd5360cba1adbba.html

    Cayley定理在组合数学中的应用。

    View Code
    #include <iostream>
    #include <cmath>
    #include <cstring>
    #include <cstdio>
    #include <string>
    #include <stdlib.h>
    using namespace std;
    typedef long long LL;
    const LL M=1e9+7;
    LL Pow( LL a, LL b)
    {
        LL ans=1;
        while( b ){
            if( b&1 ){
                ans*=a;
                ans%=M;
            }
            a*=a;
            a%=M;
            b>>=1; 
        }
        return ans;
    } 
    int main( )
    {
        LL N;
        while( scanf( "%lld", &N )!=EOF ){
            printf( "%lld\n", Pow( N, N-2 ) );
        }
        return 0;
    }

     

    C.计算几何。该题一个简单的做法是先找出三个点的外心,然后用极坐标每次旋转72°生成剩下的点。

    View Code
    #include <cstring>
    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <iostream>
    #include <cmath>
    #include <vector>
    using namespace std;
    
    const double PI = acos(-1);
    
    struct point{double x,y;};
    struct line{point a,b;};
    vector<point>v;
    
    point p[3], cc;
    
    point intersection(line u,line v){
        point ret=u.a;
        double t=((u.a.x-v.a.x)*(v.a.y-v.b.y)-(u.a.y-v.a.y)*(v.a.x-v.b.x))
            /((u.a.x-u.b.x)*(v.a.y-v.b.y)-(u.a.y-u.b.y)*(v.a.x-v.b.x));
        ret.x+=(u.b.x-u.a.x)*t;
        ret.y+=(u.b.y-u.a.y)*t;
        return ret;
    }
    
    point circumcenter(point a,point b,point c){
        line u,v;
        u.a.x=(a.x+b.x)/2;
        u.a.y=(a.y+b.y)/2;
        u.b.x=u.a.x-a.y+b.y;
        u.b.y=u.a.y+a.x-b.x;
        v.a.x=(a.x+c.x)/2;
        v.a.y=(a.y+c.y)/2;
        v.b.x=v.a.x-a.y+c.y;
        v.b.y=v.a.y+a.x-c.x;
        return intersection(u,v);
    }
    
    double dist(point a, point b) {
        return sqrt(double( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) )); 
    }
    
    bool cmp(point a, point b) {
        if (fabs(a.x - b.x) > 1e-6) {
            return a.x < b.x;
        } else {
            return a.y < b.y;
        }
    }
    
    int main() {
        double R;
        point info;
        while (scanf("%lf %lf", &p[0].x, &p[0].y) != EOF) {
            v.clear();
            for (int i = 1; i < 3; ++i) {
                scanf("%lf %lf", &p[i].x, &p[i].y);
            }
            cc = circumcenter(p[0], p[1], p[2]);
            R = dist(p[0], cc);
            double phi = atan2(p[0].y-cc.y, p[0].x-cc.x); // 求出第一个点的角度
            for (int i = 0; i < 5; ++i) {
                info.x = R * cos(phi) + cc.x, info.y = R * sin(phi) + cc.y;
                v.push_back(info);
                phi += 72.0*PI/180.0;
            }
            sort(v.begin(), v.end(), cmp);
            for (int i = 0; i < 5; ++i) {
                int flag = 0;
                for (int j = 0; j < 3; ++j) {
                    if (fabs(v[i].x - p[j].x) < 1e-6 && fabs(v[i].y - p[j].y) < 1e-6) {
                        flag = 1;
                        break;
                    }
                }
                if (!flag) {
                    printf("%.2f %.2f\n", v[i].x, v[i].y);
                }
            }
        }
        return 0;    
    }

     也可以通过圆求对称点来解

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<math.h>
    #include<algorithm>
    using namespace std;
     
     
    const double esp = 1e-8;
    int sign( double x )
    {
        return x<-esp ? -1 : (x > esp); 
    }
     
     
    struct Point 
    {
        double x, y;
        void read(){scanf("%lf%lf",&x,&y);}
        bool operator < (Point tmp) const
        {
            if( sign(x-tmp.x) == 0 ) return y < tmp.y;
            else    return x < tmp.x;
        }
    }p[5], mid, ans[2];
     
    double L;
     
    void get_middle_point(double &x,double &y,double x1,double y1,double x2,double y2,double x3,double y3)
    {
        double tempx1,tempx2,tempy1,tempy2;
        double k1,k2;
        if(y1==y2)
        {
            x=(x1+x2)/2.0;
            tempx1=(x1+x3)/2.0;
            tempy1=(y1+y3)/2.0;
            k1=(x3-x1)/(y1-y3);
            y=k1*(x-tempx1)+tempy1;
        }
        else if(y1==y3)
        {
            x=(x1+x3)/2.0;
            tempx1=(x1+x2)/2.0;
            tempy1=(y1+y2)/2.0;
            k1=(x2-x1)/(y1-y2);
            y=k1*(x-tempx1)+tempy1;
        }
        else if(y2==y3)
        {
            x=(x2+x3)/2.0;
            tempx1=(x1+x3)/2.0;
            tempy1=(y1+y3)/2.0;
            k1=(x3-x1)/(y1-y3);
            y=k1*(x-tempx1)+tempy1;
        }
        else
        {
            //printf("i am here\n");
            tempx1=(x1+x3)/2.0;
            tempy1=(y1+y3)/2.0;
            k1=(x3-x1)/(y1-y3);
            tempx2=(x1+x2)/2.0;
            tempy2=(y1+y2)/2.0;
            k2=(x2-x1)/(y1-y2);
            //printf("%lf %lf %lf %lf %lf %lf\n",tempx1,tempy1,k1,tempx2,tempy2,k2);
            x=((tempy2-tempy1)+k1*tempx1-k2*tempx2)/(k1-k2);
            y=k1*(x-tempx1)+tempy1;
        }
    }
    Point duicheng( Point P, Point a1, Point a2 )
    {
        Point res;
        double x = P.x, y = P.y;
        double x1 = a1.x, y1 = a1.y;
        double x2 = a2.x, y2 = a2.y;
        if( y1 == y2 || x1 == x2 )
        {
            if( x1 == x2){ res.x =  2*x1-x; res.y = y; }    
            else{   res.x = x; res.y = 2*y1-y;}
            return res; 
        }
        else{
            double k = (y1-y2)/(x1-x2), b = y1 - x1*k;
            res.x = 2*(x+k*y-k*b)/(k*k+1)-x;
            res.y = 2*(k*x+k*k*y+b)/(k*k+1)-y;
            return res; 
        }
    }
    double dist( Point a, Point b )
    {
        double dis = (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
        return sqrt(dis);
    }
     
    bool compare( Point a, Point b )
    {
        for(int i = 0; i < 3; i++)
        {
            if( (sign( a.x-p[i].x ) == 0) && (sign( a.y-p[i].y)) == 0 ) 
                return false;
            if( (sign( b.x-p[i].x ) == 0) && (sign( b.y-p[i].y)) == 0 ) 
                return false;
        }
        return true;
    }
    int main()
    {
        int T;
        scanf("%d", &T);
        while( T-- )
        {
            for(int i = 0; i < 3; i++)
                p[i].read();
            get_middle_point( mid.x, mid.y, p[0].x,p[0].y,p[1].x,p[1].y,p[2].x,p[2].y); 
            L = dist( mid, p[0] );  
            for(int i = 0; i < 3; i++)
            {
                ans[0] = duicheng( p[(i+1)%3], p[i],mid );  
                ans[1] = duicheng( p[(i+2)%3], p[i],mid );  
                if( compare(ans[0],ans[1]) ) break; 
            }
            sort( ans, ans+2 );
            printf("%.2f %.2f\n", ans[0].x, ans[0].y );
            printf("%.2f %.2f\n", ans[1].x, ans[1].y );
        }
        return 0;
    }

    D.动态规划。题意是求将一个串改造成回文串的最少花费。给出所有组成串的字母添加和删除的花费。设dp[i][j]表示[i,j]子串被改造成回文串的最少开销是多少。那么有动态规划方程:

    dp[i][j] = min(dp[i+1][j] + cost[i], dp[i][j-1] + cost[j]); 其中cost[i] = min(add[i], del[i])存储删除和添加节点的较小的花费。如果str[i] == str[j]的话就多出一种选择dp[i-1][j-1]

    View Code
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    int N, M, dp[2005][2005];
    int cost[30];
    char str[2005];
    
    int DP() {
        for (int i = 1; i < M; ++i) {
            for (int j = 1; j+i <= M; ++j) {
                int a = j, b = j + i, ll = str[a]-'a', rr = str[b]-'a';
                dp[a][b] = min(dp[a+1][b] + cost[ll], dp[a][b-1] + cost[rr]);
                if (ll == rr)
                    dp[a][b] = min(dp[a][b], dp[a+1][b-1]);
            }
        }
        return dp[1][M];
    }
    
    int main() {
        char s[5];
        int a, b;
        while (scanf("%d %d", &N, &M) == 2) {
            scanf("%s", str+1);
            for (int i = 1; i <= N; ++i) {
                scanf("%s %d %d", s, &a, &b);
                cost[s[0]-'a'] = min(a, b);
            }
            printf("%d\n", DP());
        }
        return 0;
    }

     

    E.

     将 b1,b2,...,bn分解素因子,化成  形式.

      总共有 K 种不同的 素因子

      令,  表示 第i 种素因子的次数.

      则, 我们可以先分开考虑第 i 种素数,其共有m个,将其放置在 n个盒子中,其中盒子不同,可以为空.

      这里有一个 组合数学关于 球放置于盒子的 问题.    具体可参考 jian1573博客 (http://www.cnblogs.com/jian1573/archive/2011/09/18/2180433.html

      其方案数为 

      因为总共有 K 种 不同素数, 则总方案数为:

        

      此时问题,还没有被完全解决, 因为, 题目要求 ai > 1, 意味着, 任意个盒子,不可同时都为空, 

      而我们上面的计算,是先假设其可以为空的情形.所以,我们需要减去所有为空的情况,剩下的才是我们的最终结果.

      这里通过 假设,至少1个盒子为空, 至少2个盒子为空, ...,至少 n-1个盒子为空( 必定有1个盒子不为空,所以不能取到n )

      使用容斥原理来计算.   关键点

        一, 假设 1个盒子必定为空, 我们可以通过 假定盒子总数量为 n-1 个, 公式转换成   得出. 多个盒子为空同上.

        二, 假设 1个盒子必定为空, 对于剩下的 n-1个盒子, 因为我们计算的是组合情形, 其中还是会出现其它盒子为空.意味着

    有重复的情形. 所以这里需要用到容斥原理,  另外这里因为是 集合的并,   容斥的计算是 减奇加偶.

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<assert.h>
    #include<algorithm>
    #include<map>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    
    const int mod = 1e9+7;
    
    int b[21], a[1010], n, tot;
    LL C[1100][1100];
    
    map<int,int>mp;
    
    void init(){
        for(int i = 0; i <= 1000; i++)
            C[i][0] = 1, C[i][i] = 1;
        for(int i = 2; i <= 1000; i++)
            for(int j = 1; j < i; j++)
                C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
    }
    
    void deal(){
        mp.clear();
        for(int i = 0; i < n; i++){
            
            int tmp_max = (int)sqrt(1.*b[i]);
            int t = b[i];
            for(int j = 2; j <= tmp_max; j++)
            {
                if( t%j == 0 ){
                    if( mp.count(j) == 0 ) mp[j] = 0;
    
                    int cnt = 0;    
                    while( t%j == 0 ) (t/=j), cnt++;
                    mp[j] += cnt;
                } 
            }
            if( t > 1 ){
                if( mp.count(t) == 0 ) mp[t] = 1;
                else    mp[t]++;
            }    
        }
        int idx = 0;
        memset( a, 0, sizeof(a));
        for( map<int,int>::iterator it = mp.begin(); it != mp.end(); it++ )
            a[idx++] = it->second;
        tot = mp.size();
    }
    void solve(){
        LL ans = 0;
            
        for(int i = 0; i < n; i++){
            LL tmp = 1;
            for(int j = 0; j < tot; j++){    
                assert( (((n-1-i+a[j])>=0)&&(n-1-i+a[j])<=1000) );    
                tmp = tmp*C[ n-1-i+a[j] ][ n-1-i ]%mod;    
            }
            tmp = tmp*C[n][i]%mod;
            
            if( i&1 ) tmp = -tmp;
            ans = (ans+tmp+mod)%mod;
        }
        printf("%lld\n", ans);
    }
    int main(){
        init();
        int T;
        scanf("%d", &T);
        while( T-- ){
            scanf("%d", &n);
            for(int i = 0; i < n; i++)
                scanf("%d", &b[i] );
            deal();
            solve();
        }
        return 0;
    }

    F.并查集+最短路。通过并查集处理结盟的点,然后从集合中选择一个到目标点最短的距离输出。

     解法一:

    View Code
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    
    using namespace std;
    
    const int  inf = 0x7f7f7f7f;
    const int MAXV = 1002;
    
    int UFS[MAXV];
    
    int Find(int x)
    {
        return UFS[x]=(UFS[x]!=x?Find(UFS[x]):x);
    }
    void Union(int x,int y)
    {
        UFS[Find(x)]=Find(y);
    }
    
    char o;
    int n,m,k,u,v,w,x,y,a[MAXV][MAXV];
    
    int main()
    {
        while(scanf("%d %d %d",&n,&m,&k)!=EOF)
        {
            memset(a,inf,sizeof(a));
            for(int i=0;n>i;i++)
            {
                UFS[i]=i;
            }
            for(int i=0;m>i;i++)
            {
                scanf("%d %d %d",&u,&v,&w);
                a[u][v]=min(a[u][v],w);
            }
            for(int z=0;n>z;z++)
            {
                for(int i=0;n>i;i++)
                {
                    if(a[i][z]!=inf)
                    {
                        for(int j=0;n>j;j++)
                        {
                            if(a[z][j]!=inf)
                            {
                                a[i][j]=min(a[i][j],a[i][z]+a[z][j]);
                            }
                        }
                    }
                }
            }
            for(int i=0;k>i;i++)
            {
              //  getchar();
                  char ch[10];
                scanf("%s %d %d",&ch,&x,&y); o = ch[0];
                if(o=='I') Union(x,y);
                else
                {
                    int ans=inf;
                    for(int i=0;n>i;i++)
                    {
                        if(Find(x)==Find(i))
                        {
                            ans=min(ans,a[i][y]);
                        }
                    }
                    printf("%d\n",ans!=inf?ans:-1);
                }
            }
        }
        return 0;
    }

    解法二:

    View Code
    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    #include<queue>
    
    using std::queue;
    
    const int inf = 0x7f7f7f7f;
    
    const int MAXV = 1002;
    const int MAXE = 500002;
    
    struct node
    {
        int v,w;
    }G[MAXE];
    int _index,pre[MAXV],next[MAXE];
    
    void clear(void)
    {
        _index=0;
        memset(pre,-1,sizeof(pre));
    }
    void add(int u,int v,int w)
    {
        G[_index].v=v;
        G[_index].w=w;
        next[_index]=pre[u];
        pre[u]=_index++;
    }
    
    int UFS[MAXV];
    
    int Find(int x)
    {
        return UFS[x]=(UFS[x]!=x?Find(UFS[x]):x);
    }
    void Union(int x,int y)
    {
        UFS[Find(x)]=Find(y);
    }
    
    int dis[MAXV];
    bool inQ[MAXV];
    
    int SPFA(int src,int des,int n)
    {
        queue<int> Q;
        memset(dis,inf,sizeof(dis));
        memset(inQ,false,sizeof(inQ));
    
        for(int i=0;n>i;i++)
        {
            if(Find(i)==Find(src))
            {
                dis[i]=0;
                inQ[i]=true;
                Q.push(i);
            }
        }
    
        int u,v,w;
        while(!Q.empty())
        {
            u=Q.front();
            inQ[u]=false;
            Q.pop();
            for(int i=pre[u];i!=-1;i=next[i])
            {
                v=G[i].v;
                w=G[i].w;
                if(dis[v]>dis[u]+w)
                {
                    dis[v]=dis[u]+w;
                    if(!inQ[v])
                    {
                        inQ[v]=true;
                        Q.push(v);
                    }
                }
            }
        }
    
        return dis[des]!=inf?dis[des]:-1;
    }
    
    char o;
    int n,m,k,u,v,w;
    
    int main()
    {
        while(scanf("%d %d %d",&n,&m,&k)!=EOF)
        {
            clear();
            for(int i=0;n>i;i++)
            {
                UFS[i]=i;
            }
            for(int i=0;m>i;i++)
            {
                scanf("%d %d %d",&u,&v,&w);
                add(u,v,w);
            }
            for(int i=0;k>i;i++)
            {
                scanf("\n%c %d %d",&o,&u,&v);
                if(o=='I') Union(u,v);
                else printf("%d\n",SPFA(u,v,n));
            }
        }
        return 0;
    }

    G.二分枚举构图+二分匹配。该题想说明的就是A中的一个城市只能够对应B中的一个城市。先通过floyd求出任意城市之间的最短路,然后二分枚举构好A->B城市的二分图,然后使用匈牙利算法计算是否能够达到完全匹配。

    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <ctime>
    #include <cassert>
    using namespace std;
    
    const int MAX = 105;
    const int INF = 0x3f3f3f3f;
    int n, N, M, reca[MAX], recb[MAX];
    int mp[MAX][MAX];
    int marry[MAX];
    char G[MAX][MAX];
    char vis[MAX];
    
    void floyd() {
        for (int k = 0; k < n; ++k) {
            for (int i = 0; i < n; ++i) {
                if (mp[i][k] == INF || i == k) continue;
                for (int j = 0; j < n; ++j) {
                    if (mp[k][j] == INF || j == k) continue;
                    if (mp[i][j] > mp[i][k] + mp[k][j]) {
                        mp[i][j] = mp[i][k] + mp[k][j];
                    }
                }
            }    
        }
    }
    
    bool path(int u) 
    {
        for (int i = 0; i < M; ++i) {
            if (!G[u][i] || vis[i])
                continue;
            vis[i] = 1;
            if (marry[i] == -1 || path(marry[i])) {
                marry[i] = u;
                return true;
            }
        }
        return false;
    }
    
    void build(int lim) {
        memset(G, 0, sizeof (G));
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < M; ++j) {
                if (mp[reca[i]][recb[j]] <= lim) {
                    G[i][j] = 1;
                }
            }
        }
    }
    
    bool Ac() {
        int cnt = 0;
        memset(marry, 0xff, sizeof (marry));
        for (int i = 0; i < N; ++i) {
            memset(vis, 0, sizeof (vis));
            if (path(i)) {
                ++cnt;
            }
        }
        return cnt == M;
    }
    
    int bsearch(int l, int r) {
        int mid, ret;
        while (l <= r) {
            mid = (l + r) >> 1;
            build(mid);
            if (Ac()) {
                ret = mid;
                r = mid - 1;    
            } else {
                l = mid + 1;
            }
        }
        return ret;
    }
    
    int main()
    {
        while (scanf("%d", &n) == 1) {
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    scanf("%d", &mp[i][j]);
                }
            }
            scanf("%d", &N);
            for (int i = 0; i < N; ++i) {
                scanf("%d", &reca[i]);
                --reca[i];
            }
            scanf("%d", &M);
            for (int i = 0; i < M; ++i) {
                scanf("%d", &recb[i]);
                --recb[i];
            }
            floyd();
            printf("%d\n", bsearch(0, 10000));
        }
        return 0;
    }

     

    H.

    法一:通过题目给定的关系不难知道a[i] = 3*a[i-1] + a[i-2] - 3*a[i-3]。假设要求第D天的口令,那么计算出a[D-1], a[D-2], a[D-3]就能够计算出a[D],求出四个系数后,代入X便可求解答案了。这题D给的数据比较大,直接计算肯定会TLE,那么使用矩阵并加上快速降幂来计算就简单多了,将给定的b,c,d作为3*1的矩阵B{a[0], a[-1], a[-2]}^-1,矩阵A=
    3 1 -3

    1 0 0

    0 1 0

    作为递推矩阵,那么在B矩阵左边乘上D-1A最后的结果就是{a[D-1], a[D-2], a[D-3]}^-1D-1A相乘就可以使用矩阵快速幂了。

     

    View Code
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert> 
    using namespace std;
    
    typedef long long int64;
    const int MOD = int(1e9)+7;
    int a, b, c, d, D, X;
    
    int64 R[3][3] = {
        {3, 1, -3},
        {1, 0, 0},
        {0, 1, 0}
    };
    
    struct Matrix {
        int64 mat[3][3];
        Matrix() {
            memset(mat, 0, sizeof (mat));
        }
        void unit() {
            memset(mat, 0, sizeof (mat));
            for (int i = 0; i < 3; ++i) {
                mat[i][i] = 1;
            }
        }
        void init() {
            memcpy(mat, R, sizeof (mat));
        }
        void show() const;
        friend Matrix operator * (const Matrix & a, const Matrix & b);
        friend Matrix pow(Matrix a, int b);
    };
    
    void Matrix::show() const {
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                printf("%d ", mat[i][j]);    
            }
            puts("");
        }
    }
    
    Matrix operator * (const Matrix & a, const Matrix & b) {
        Matrix ret;
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                for (int k = 0; k < 3; ++k) {
                    ret.mat[i][j] += (a.mat[i][k] * b.mat[k][j]) % MOD;
                    ret.mat[i][j] %= MOD;
                }
            }
        }
        return ret;
    }
    
    Matrix pow(Matrix a, int b) {
        Matrix ret;
        ret.unit();
        while (b) {
            if (b & 1) {
                ret = ret * a;
            }
            a = a * a;
            b >>= 1;
        }
        return ret;
    }
    
    int64 cal(int64 na, int64 nb, int64 nc, int64 nd) {
        int64 ini[4] = {1};
        for (int i = 1; i < 4; ++i) {
            ini[i] = (ini[i-1] * X) % MOD;
        }
        return ((na*ini[3])%MOD+(nb*ini[2])%MOD+(nc*ini[1])%MOD+(nd*ini[0])%MOD)%MOD;
    }
    
    void solve() {
        Matrix mm; 
        mm.init();
        mm = pow(mm, D-1);
        int64 na, nb, nc, nd;
        nb = ((mm.mat[0][0]*b)%MOD + (mm.mat[0][1]*c)%MOD + (mm.mat[0][2]*d)%MOD)%MOD;
        nc = ((mm.mat[1][0]*b)%MOD + (mm.mat[1][1]*c)%MOD + (mm.mat[1][2]*d)%MOD)%MOD;
        nd = ((mm.mat[2][0]*b)%MOD + (mm.mat[2][1]*c)%MOD + (mm.mat[2][2]*d)%MOD)%MOD;
        na = (3*nb)%MOD+nc-(3*nd)%MOD;
        printf("%d\n", int((cal(na, nb, nc, nd)+MOD)%MOD));
    }
    
    int main() {
        while (scanf("%d %d %d %d", &a, &b, &c, &d) != EOF) {
            assert(a >= 0 && a <= 100);
            scanf("%d %d", &D, &X);
            solve();
        }
        return 0;
    } 

    法二:根据表达式a[i] = 3*a[i-1] + a[i-2] - 3*a[i-3],由于该表达式为常系数3阶齐次递推关系,因此设a[n] = r^n,代入方程求出特征根为-1,1,3,因此a[n]=A*(-1)^n+B*(1)^n+C*(3)^n,令a[0]=d,a[1]=c,a[2]=b求出A,B,C之后再根据通项公式求出D天后的系数。b,c,d8的倍数保证了解三元一次方程组时不会出现分数。

     

    View Code
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    
    typedef long long int64;
    const int64 MOD = (int64)1e9 + 7;
    int64 a, b, c, d, D, X;
    
    int64 _pow(int64 a, int64 b) {
        int64 ret = 1;
        while (b) {
            if (b & 1) {
                ret *= a;
                ret %= MOD;
            }
            b >>= 1;
            a *= a;
            a %= MOD;
        }
        return ret;
    }
    
    int64 cal(int64 na, int64 nb, int64 nc, int64 nd) {
        int64 ini[4] = {1};
        for (int i = 1; i < 4; ++i) {
            ini[i] = (ini[i-1] * X) % MOD;
        }
        return ((na*ini[3])%MOD+(nb*ini[2])%MOD+(nc*ini[1])%MOD+(nd*ini[0])%MOD)%MOD;
    }
    
    int main() {
        while (scanf("%lld %lld %lld %lld", &a, &b, &c, &d) != EOF) {
            scanf("%lld %lld", &D, &X);
            int64 A = (3*d-4*c+b)/8;
            int64 B = (3*d+2*c-b)/4;
            int64 C = (b-d)/8;
            a = (A * ((D+2) & 1 ? -1 : 1) + B + C * _pow(3, D+2)) % MOD;
            b = (A * ((D+1) & 1 ? -1 : 1) + B + C * _pow(3, D+1)) % MOD;
            c = (A * ((D) & 1 ? -1 : 1) + B + C * _pow(3, D)) % MOD;
            d = (A * ((D-1) & 1 ? -1 : 1) + B + C * _pow(3, D-1)) % MOD;
            printf("%lld\n", (cal(a, b, c, d)+MOD)%MOD);
        }
        return 0;    
    }

    PS:感谢 “|wo只洗碗、不吃fan” 对H题提供新的解题思路。

  • 相关阅读:
    【杂谈】操作系统如何有效地掌控CPU
    【API知识】一种你可能没见过的Controller形式
    【详解】Tomcat是如何监控并删除超时Session的?
    【API知识】RestTemplate的使用
    【杂谈】Tomcat 之 Lifecycle接口
    【杂谈】FilterChain相关知识整理
    【杂谈】Remember-Me的实现
    【杂谈】没有公网IP的电脑如何与外部通信
    【杂谈】tocmat是何时写回响应数据报的
    js的class基础
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2990372.html
Copyright © 2011-2022 走看看