zoukankan      html  css  js  c++  java
  • 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!!

    官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解)

    A. 树链剖分数据结构板题

    题目大意:我没看,看不懂。

    基本思路:我不会。

    参考代码:找Oyk老师和Czj老师去。

    B. The background of water problem

    题目大意(大写加粗的水题):给定$N$个学生和他们$K$个科目的成绩$S_i$,再给出各科目$K_i$的权重顺序$Q_i$,求排名之后,拥有id为$X$的是哪个学生。

    基本思路:虽然$K$只有$10$,$S$只有$100$,但有M组查询,所以当然不能开个long long去hash每个学生。我们简单点,开个结构体,排个序,就好了。

    参考代码

    官方代码(将成绩和id分开放,避免在排序时复制构造大结构体):

     1 #include <cstdio>
     2 #include <string.h>
     3 #include <algorithm>
     4 #include <vector>
     5 using namespace std;
     6 
     7 int N,K,M,X;
     8 int people[1005][11];
     9 int cmpOrder[11];
    10 
    11 struct CmpNode{
    12     CmpNode(int x):id(x){}
    13     int id;
    14     bool operator < (const CmpNode &other) const
    15     {
    16         for(int i=0; i<K; i++)
    17         {
    18             if(people[this->id][cmpOrder[i]] > people[other.id][cmpOrder[i]])
    19                 return true;
    20             else if(people[this->id][cmpOrder[i]] < people[other.id][cmpOrder[i]])
    21                 return false;
    22         }
    23         return this->id<other.id;
    24     }
    25 };
    26 
    27 void solve(FILE *fin=stdin, FILE *fout=stdout)
    28 {
    29     int t;
    30     fscanf(fin,"%d",&t);
    31     while(t--)
    32     {
    33         fscanf(fin,"%d%d",&N,&K);
    34         vector<CmpNode> nodes;
    35         for(int i=0;i<N;i++)
    36         {
    37             nodes.push_back(CmpNode(i));
    38             for(int j=1;j<=K;j++)
    39                 fscanf(fin,"%d",&people[i][j]);
    40         }
    41         fscanf(fin,"%d",&M);
    42         while(M--)
    43         {
    44             for(int i=0;i<K;i++)
    45                 fscanf(fin,"%d",cmpOrder+i);
    46             fscanf(fin,"%d", &X);
    47             sort(nodes.begin(),nodes.end());
    48             fprintf(fout,"%d
    ",nodes[X-1].id+1);
    49         }
    50     }
    51 }
    52 
    53 int main()
    54 {
    55     solve(stdin,stdout);
    56     return 0;
    57 }
    B. The background of water problem

    非官方代码(这是通通放在结构体的例子,无论算法竞赛还是工程都不建议这么排序):

     1 #include <stdio.h>
     2 #include <algorithm>
     3 
     4 int N, K, M, X;
     5 int order[11];
     6 
     7 struct stu {
     8     int id, score[11];
     9     bool operator <(const stu&x) const {
    10         for(int i=1; i<=K; i++)
    11             if(score[order[i]] != x.score[order[i]])
    12                 return score[order[i]] > x.score[order[i]];
    13         return id < x.id;
    14     }
    15 }student[1001];
    16 
    17 int main() {
    18     int T;
    19     scanf("%d", &T);
    20     while(T--) {
    21         scanf("%d%d", &N, &K);
    22         for(int i=1; i<=N; i++) {
    23             student[i].id = i;
    24             for(int j=1; j<=K; j++)
    25                 scanf("%d", &student[i].score[j]);
    26         }
    27         scanf("%d", &M);
    28         while(M--) {
    29             for(int i=1; i<=K; i++)
    30                 scanf("%d", order+i);
    31             std::sort(student+1, student+1+N);
    32             scanf("%d", &X);
    33             printf("%d
    ", student[X].id);
    34         }
    35     }
    36     return 0;
    37 }
    B. The background of water problem 

    C. Oyk cut paper forever

    题目大意

      永远的Oyk剪纸(大雾)。Oyk给面子Z大师,玩$C$轮剪纸,每轮给定一条长为$k$个单位的纸带,Z大师先手可以剪去(任意)$N_1$个单位,但不能不剪或全部拿走。此后每轮都只能剪$1$到$2 imes N_1$个单位,能拿走最后一段纸带的人获胜,问Oyk第一次获胜是第几轮。

    基本思路

      斐波那契博弈,证明参见:

      原题参见 hdoj 2516. 取石子游戏 等。

      首先知道了这是个斐波那契博弈,接下来我们要做的就是判断$K_i$是否为Fibonacci数。容易想到的是用递推打一个表,将Fibonacci数存起来或标记一下。但是我们知道斐波那契数列通项公式为$F_n=frac1{sqrt5}left[left(frac{1+sqrt5}2 ight)^n-left(frac{1-sqrt5}2 ight)^n ight]$(比内公式),还知道判断一个数$x$是否为Fibonacci数只需判断$5x^2+4$或$5x^2-4$是否为完全平方数(参考:Wiki示例)(即判断开根号后是否为整数),于是Over。

    参考代码

    官方代码(丧sha病bi出题人改了好几波代码,我们还是假装下面的是对的吧):

     1 #include <cstdlib>
     2 #include <cstdio>
     3 #include <cmath>
     4 using namespace std;
     5 
     6 int main()
     7 {
     8     int T;
     9     scanf("%d",&T);
    10     while(T--){
    11         int n,i;
    12         int ans=0;
    13         scanf("%d",&n);
    14         for(i = 1;i<=n;i++){
    15             int a;
    16             scanf("%d",&a);
    17             if(!ans&&(sqrt(5*a*a+4)-(int)sqrt(5*a*a+4)<1e-6||sqrt(5*a*a-4)-(int)sqrt(5*a*a-4)<1e-6)){
    18                 ans=i;
    19             }
    20         }
    21         if(ans)printf("%d
    ",ans);
    22         else puts("Oyk forever!");
    23     }
    24     return 0;
    25 }
    C. OykOyk!

    非官方代码(打表出奇迹):

     1 #include <stdio.h>
     2 using namespace std;
     3 
     4 int flag[100100];
     5 void init() {
     6     int a = 1, b = 2;
     7     while(b<=100000) {
     8         ++flag[b];
     9         b += a;
    10         a = b-a;
    11     }
    12 }
    13 int main() {
    14     int T; init();
    15     scanf("%d", &T);
    16     while(T--) {
    17         int C, k, res=0;
    18         scanf("%d", &C);
    19         for(int i=1; i<=C; i++) {
    20             scanf("%d", &k);
    21             if(!res&&flag[k])
    22                 res = i;
    23         }
    24         res?printf("%d
    ", res):puts("Oyk forever!");
    25     }
    26     return 0;
    27 }
    C. Oyk forever!

     

    D. 最小费用流

    题目大意

      挖$n$天的宝藏,每天需要$R_i$只铲子,当天用完会坏掉。有$3$种方式准备铲子:从商店买,$p$元一只;找铁匠$A$修理,每只$f$元同时修$m$天;找铁匠$B$修理,每只$s$元同时修$t$天。问最小花费。

    基本思路

      最小费用流,参见网络流24题之餐巾问题。

    参考代码

      1 #include <iostream>
      2 #include<stdio.h>
      3 #include<string.h>
      4 #define INF 99999999
      5 #define min(x,y) (x)>(y)?(y):(x)
      6 #define abs(x) ((x)>0?(x):-(x))
      7 #define E 50000
      8 struct p
      9 {
     10     int v,next,k,t,cost;
     11 }edge[200000];
     12 int n,m,ans,tot,S,T,head[1001],pre[1001],pid[1001],pop[100001];
     13 int mark[1001],dis[1001],now[1001];
     14 void addedge(int a,int b,int k,int cost)
     15 {
     16     edge[tot].v=b;
     17     edge[tot].k=k;
     18     edge[tot].cost=cost;
     19     edge[tot].t=tot+1;
     20     edge[tot].next=head[a];
     21     head[a]=tot++;
     22 
     23     edge[tot].v=a;
     24     edge[tot].k=0;
     25     edge[tot].cost=-cost;
     26     edge[tot].t=tot-1;
     27     edge[tot].next=head[b];
     28     head[b]=tot++;
     29 }
     30 int spfa()
     31 {
     32     int i,top,tail,cur;
     33     for(i=0;i<=T;i++)
     34         dis[i]=INF,mark[i]=0;
     35     top=tail=0;
     36     pop[top++]=S;
     37     dis[S]=0;
     38     mark[S]=1;
     39     while(tail!=top)
     40     {
     41         cur=pop[tail++];
     42         tail%=50000;
     43         mark[cur]=0;
     44         for(i=head[cur];i!=-1;i=edge[i].next)
     45             if(edge[i].k>0&&dis[edge[i].v]>dis[cur]+edge[i].cost)
     46             {
     47                 dis[edge[i].v]=dis[cur]+edge[i].cost;
     48                 pre[edge[i].v]=cur;
     49                 pid[edge[i].v]=i;
     50                 if(mark[edge[i].v]==0)
     51                 {
     52                     mark[edge[i].v]=1;
     53                     pop[top++]=edge[i].v;
     54                     top%=50000;
     55                 }
     56             }
     57     }
     58     return dis[T];
     59 }
     60 int mincost()
     61 {
     62     int i,flow,tmp,ans,maxflow=0;
     63     ans=0;
     64     while(1)
     65     {
     66         tmp=spfa();
     67         if(tmp==INF) break;
     68         flow=INF;
     69         for(i=T;i!=S;i=pre[i])
     70             if(edge[pid[i]].k<flow)
     71                 flow=edge[pid[i]].k;
     72         for(i=T;i!=S;i=pre[i])
     73         {
     74             edge[pid[i]].k-=flow;
     75             edge[edge[pid[i]].t].k+=flow;
     76         }
     77         maxflow+=flow;
     78         ans+=tmp*flow;
     79     }
     80     return ans;
     81 }
     82 int main()
     83 {
     84     freopen("in.txt","r",stdin);
     85     freopen("out.txt","w",stdout);
     86     int i,j,p,m1,f,m2,s,tmp;
     87     while(scanf("%d%d%d%d%d%d",&n,&p,&m1,&f,&m2,&s)!=EOF)
     88     {
     89         memset(edge,0xff,sizeof(edge));
     90         tot=n*2+3;
     91         S=0;
     92         T=n*2+1;
     93         for(i=1;i<=n;i++)
     94         {
     95             scanf("%d",&tmp);
     96             tmp++;
     97             addedge(S,i+n,INF,p);
     98             addedge(S,i,tmp,0);
     99             addedge(i+n,T,tmp,0);
    100             if(i<n) addedge(i,i+1,INF,0);
    101             if(i+m1<=n) addedge(i,n+i+m1,INF,f);
    102             if(i+m2<=n) addedge(i,n+i+m2,INF,s);
    103         }
    104         printf("%d
    ",mincost());
    105     }
    106     return 0;
    107 }
    D. Dig the treasure

    E. Wwj's work

    题目大意:这题是HDOJ 4622. Reincarnation原题,有且仅有数据是自己造的。。

    基本思路:求一个字符串的子串数目,标准的后缀自动机。当然似乎也可以后缀数组、后缀xxx什么的乱搞。

    参考代码:(参考kuangbin的模板,和代码

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <algorithm>
     4 using namespace std;
     5 
     6 const int CHAR = 26;
     7 const int MAXN = 2020;
     8 struct SAM_Node {
     9     SAM_Node *fa, *next[CHAR];
    10     int len;
    11     int id,pos;
    12     SAM_Node() {}
    13     SAM_Node(int _len) {
    14         fa = 0;
    15         len = _len;
    16         memset(next,0,sizeof(next));
    17     }
    18 };
    19 SAM_Node SAM_node[MAXN*2], *SAM_root, *SAM_last;
    20 int SAM_size;
    21 SAM_Node *newSAM_Node(int len) {
    22     SAM_node[SAM_size] = SAM_Node(len);
    23     SAM_node[SAM_size].id = SAM_size;
    24     return &SAM_node[SAM_size++];
    25 }
    26 SAM_Node *newSAM_Node(SAM_Node *p) {
    27     SAM_node[SAM_size] = *p;
    28     SAM_node[SAM_size].id = SAM_size;
    29     return &SAM_node[SAM_size++];
    30 }
    31 void SAM_init() {
    32     SAM_size = 0;
    33     SAM_root = SAM_last = newSAM_Node(0);
    34     SAM_node[0].pos = 0;
    35 }
    36 void SAM_add(int x,int len) {
    37     SAM_Node *p = SAM_last, *np = newSAM_Node(p->len+1);
    38     np->pos = len;
    39     SAM_last = np;
    40     for(; p && !p->next[x]; p = p->fa)
    41         p->next[x] = np;
    42     if(!p) {
    43         np->fa = SAM_root;
    44         return;
    45     }
    46     SAM_Node *q = p->next[x];
    47     if(q->len == p->len + 1) {
    48         np->fa = q;
    49         return;
    50     }
    51     SAM_Node *nq = newSAM_Node(q);
    52     nq->len = p->len + 1;
    53     q->fa = nq;
    54     np->fa = nq;
    55     for(; p && p->next[x] == q; p = p->fa)
    56         p->next[x] = nq;
    57 }
    58 
    59 int sub[MAXN][MAXN];
    60 char S[MAXN];
    61 void read() {
    62     memset(sub, 0, sizeof(sub));
    63     scanf("%s", S);
    64     int len = strlen(S);
    65     for(int i=0; i<len; i++) {
    66         SAM_init();
    67         for(int j=i; j<len; j++)
    68             SAM_add(S[j]-'a', j-i+1);
    69         for(int j=1; j<SAM_size; j++)
    70             sub[i][ SAM_node[j].pos+i-1 ]
    71                 += SAM_node[j].len - SAM_node[j].fa->len;
    72         for(int j=i+1; j<len; j++)
    73             sub[i][j] += sub[i][j-1];
    74     }
    75 }
    76 void work() {
    77     int Q, l, r;
    78     scanf("%d", &Q);
    79     while(Q--) {
    80         scanf("%d%d", &l, &r);
    81         printf("%d
    ", sub[l-1][r-1]);
    82     }
    83 }
    84 int main() {
    85     int T;
    86     scanf("%d", &T);
    87     while(T--) {
    88         read();
    89         work();
    90     }
    91     return 0;
    92 }
    E. Wwj's work

    F. 防AK题,dfs+高斯消元

    题目大意:我没看,看不懂。

    基本思路:我不会。

    参考代码:找Czj去。

    G. Not Easy Math Problem

    题目大意:$F(m,n)=left{egin{matrix}egin{aligned}&B*2^{m-1},&n=0\&sum_{i=1}^m F(i, n-1),&n>0end{aligned}end{matrix} ight.$,其中$m<10^6$,$n<10^3$,$B<10$,求$F(m,n)\%(1E8+7)$。

    基本思路

      递推法

        先观察前面若干行若干列,发现各项系数构成杨辉三角。代几个数进去发现 $displaystyle F(m,n)=B*sum_{i=0}^{m-1}C_{m-i+n-2}^{n-1}*2^i$。

        $O(M)$肯定会TLE啊,算算算 $displaystyle frac{2F(m,n)-F(m,n)}{B}=C_{n-1}^{n-1}*2^m-C_{m+n-2}^{n-1}*2^0+sum_{i=1}^{m-1}left(C_{m-i+n-1}^{n-1}-C_{m-i+n-2}^{n-1} ight)*2^i$

        发现可以利用组合数性质,令$displaystyle T(n-1)=sum_{i=0}^{m-1}C_{m-i+n-2}^{n-1}*2^i$,$displaystyle T(n-2)=sum_{i=0}^{m-1}C_{m-i+n-2}^{n-2}*2^i$

        则$displaystyle T(n-1)=C_{n-1}^{n-1}*2^m-C_{m+n-2}^{n-1}+T(n-2)-C_{m+n-2}^{n-2}$

        递推下去得$displaystyle T(n-1)=2^{n-1}*2^m-2*sum_{i=0}^{n-2}C_{m+n-2}^i-C_{m+n-2}^{n-1}$

        来个快速幂,再预处理下逆元和组合数,$O(log(M+N)+N)$跑得飞快($log$里面的$N$可以在前面预处理组合数的循环去掉,丑就是了)。

      数学归纳法

        参见 hdoj. 5490 题解。归纳公式为$displaystyle F(m,n)=frac{q*F(m,n-1)-B*C_{m+n-2}^{n-1}}{q-1}$,然后递推,时间复杂度$O(log(M)+N)$(如果用快速幂算$2^{m-1}$的话)。

    参考代码(我的一定比标算好看):

     1 #include <stdio.h>
     2 const int MOD = 100000007;
     3 inline int add(int a, int b) { return (a%MOD+b%MOD)%MOD; }
     4 inline int sub(int a, int b) { return ((a-b)%MOD+MOD)%MOD; }
     5 inline int mul(int a, int b) { return int((long long)a%MOD*(b%MOD)%MOD); }
     6 inline int pow(int x, int n) {
     7     int res = 1;
     8     while(n) {
     9         if(n&1) res = mul(res, x);
    10         x = mul(x, x);
    11         n >>= 1;
    12     }
    13     return res;
    14 }
    15 int inv[1001]={1,1};
    16 void init() {
    17     for(int i=2; i<=1000; i++)
    18         inv[i] = mul(inv[MOD%i], MOD-MOD/i);
    19 }
    20 int B, M, N;
    21 void read() {
    22     scanf("%d%d%d", &B, &M, &N);
    23 }
    24 int Binomial[1001]={1};
    25 void work() {
    26     for(int i=1; i<N; i++)
    27         Binomial[i] = mul(mul(Binomial[i-1], M+N-i-1), inv[i]);
    28     int res = pow(2, M+N-1);
    29     for(int i=0; i<N; i++)
    30         res = sub(res, mul(Binomial[i], 2));
    31     res = mul(add(res, Binomial[N-1]), B);
    32     printf("%d
    ", res);
    33 }
    34 int main() {
    35     int T; init();
    36     scanf("%d", &T);
    37     while(T--) {
    38         read();
    39         work();
    40     }
    41     return 0;
    42 }
    比标算好看一丢丢的 G.

    H. Party!

    题目大意

      Wwj和他的女朋友们总共$N$个人去开趴体。每个人如果还没把自己的礼物送出去的话,就可以从别人手中收到礼物。第$i$个人得到第$j$人礼物的同时,也会得到一个快乐指数$H_{i, j}$。求所有人的快乐指数的总和的最大值。

    基本思路

      诶?有人用贪心做?有人用搜索做?听说还有人用最小生成树做?诶等等那个用无向图MST的什么心态?这不是有向图?(我的天呐.jpg)

      这题可以跑一个网络流,当然也可以DP。谁告诉你给一个矩阵就一定是图论题了?mdzz。

      能DP我当然不写网络流,考虑收礼物状态矩阵$M$,$M_{i, j}$代表第$i$个人收没收第$j$人的礼物($i eq j$),收了为$1$,没收为$0$。由于每个人只有一份礼物,他只能送给一个人,所以在同一列内最多只有$1$个$1$,所以状态矩阵$M$可以直接拍扁。好的,$Nleq 10$,我们可以愉快地状态压缩了,状态数$2^N-1$,dp时间复杂度$O(2^N N^2)$。

      考虑任意状态state,如果第$igeq 0$位为$1$(即$state&(1<<i)!=0$),则表明该状态下第$i$个人已经把他的礼物给出去了(当然也不可能发生给自己的情况)。对每一个状态求最大快乐指数,再取所有状态的最大值。

    参考代码

     1 #include <stdio.h>
     2 
     3 int N, H[11][11];
     4 inline void getMax(int&n, int x) { if(n<x) n=x; }
     5 void read() {
     6     scanf("%d", &N);
     7     for(int i=0; i<N; i++)
     8         for(int j=0; j<N; j++)
     9             scanf("%d", H[i]+j);
    10 }
    11 void work() {
    12     int maxState = 1<<N, dp[maxState]={0};
    13     for(int state=0; state<maxState; state++)
    14         for(int i=0; i<N; i++) if(!(state&(1<<i)))
    15             for(int j=0; j<N; j++) if(i!=j&&!(state&(1<<j)))
    16                 getMax(dp[state+(1<<j)], dp[state]+H[i][j]);
    17     int res = 0;
    18     for(int state=0; state<maxState; state++)
    19         getMax(res, dp[state]);
    20     printf("%d
    ", res);
    21 }
    22 int main() {
    23     int T;
    24     scanf("%d",&T);
    25     while(T--) {
    26         read();
    27         work();
    28     }
    29     return 0;
    30 }
    H. Party!

    I. Square

    题目大意:$N imes N$的矩阵,每个格子要填$0$或$1$,要求每行每列中$1$的个数要是奇数个。

    基本思路:不考虑限制条件,有$2^{N^2}$种方案对吧?能乱填对吧?那如何保证每行每列中$1$的个数是奇数个?在旁边加多一行加多一列(即$(N+1) imes(N+1)$),对于每一行每一列,如果$1$的个数是偶数个,再填个$1$,否则填$0$进去。啥?剩下那个格子怎么办?会一边奇数一边偶数?嗯,由于是$N imes N$,所以是不可能的。因此$S_N=2^{(N-1)^2}$,快速幂或者奇怪的优化即可。

    参考代码

    官方代码(分块处理,把47改成15,不用long long用int也是可以的,当然时间就差个2.5倍咯):

     1 #include <stdio.h>
     2 #define ULL unsigned long long
     3 int main() {
     4     ULL res;
     5     int n;
     6     int T;
     7     scanf("%d",&T);
     8     while(T--) {
     9         scanf("%d",&n);
    10         res=1;
    11         for(ULL i = 0; i<(ULL)(n-1)*(n-1)/47; i++)
    12             res=(res<<47)%100007;
    13         for(ULL i = 0; i<(ULL)(n-1)*(n-1)%47; i++)
    14             res=(res*2)%100007;
    15         printf("%d
    ",res);
    16     }
    17     return 0;
    18 }
    I. Square

    非官方代码(快速幂,怎么说也是log的复杂度,比上面奇怪的优化要快就是了):

     1 #include <stdio.h>
     2 const int MOD = 100007;
     3 long long pow(long long x, int n) {
     4     long long res = 1LL;
     5     while(n) {
     6         if(n&1) res = res*x%MOD;
     7         x = x*x%MOD;
     8         n >>= 1;
     9     }
    10     return res;
    11 }
    12 int main() {
    13     int T, N;
    14     scanf("%d",&T);
    15     while(T--) {
    16         scanf("%d",&N);
    17         --N; N *= N;
    18         printf("%d
    ", pow(2, N));
    19     }
    20     return 0;
    21 }
    I. Square

    J. Rotate and skew

    题目大意

      windows系统里面有个“画图”工具,相信大家一定不会陌生。但里面没有旋转任意$x$角度的功能,只有“扭曲”的功能。如逆时针旋转$28^circ$,我们发现可以先对$x$轴扭曲$-14^circ$,再对$y$轴扭曲$25^circ$,再对$x$轴扭曲$-14^circ$,就成功辣!问给定角度$x$,输出三次扭曲的角度。

    基本思路

      好多同学可能一开始先取个基向量,比如$vec b=(0,1)$,然后想$x$轴扭曲$-14^circ$应该是$vec{b'}=(tan14^circ,1)$,$y$轴再扭曲$25^circ$应该是$vec{b''}=(tan14^circ, 1-tan25^circ)$,再扭曲一下……再$arc tan$一下……咦?怎么出来的不是$28^circ$了?

      Naive。如果是这样那还叫扭曲吗?那叫拉伸!你倒是把$x$乘进去啊!把$y$乘进去啊!

    • 首先考虑基向量$vec a=(1,0)$。设旋转角度$ heta$。先水平扭曲任意角度,不变。垂直扭曲$varphi$,$vec{a'}=(1, tanvarphi)$,再水平扭曲一次得$vec{a''}=(1-tan(x), tanvarphi)$,和$tan heta$解一下发现$x=frac heta2$,于是知道了水平扭曲角度。
    • 正解(1):考虑向量$vec x=egin{bmatrix}x\yend{bmatrix}$,水平扭曲矩阵$A=egin{bmatrix}1&tan(-frac heta2)\0&1end{bmatrix}$, ($Avec x=egin{bmatrix}x+ytan(-frac heta2)\yend{bmatrix}$,看不懂的学线代去)
      • 三个扭曲矩阵相乘应该是$M=ABA$,其中我们要求的是中间的垂直扭曲矩阵$B$。
      • 已知旋转矩阵$M=egin{bmatrix}cos heta&-sin heta\sin heta&cos hetaend{bmatrix}$,解得$B=egin{bmatrix}1&0\sin heta&1end{bmatrix}$。
      • 但是我们的垂直扭曲矩阵应该要长成$B=egin{bmatrix}1&0\tanvarphi&1end{bmatrix}$的样子。
      • 所以我们需要用$tan$正切值去模拟$sin$正弦值(本来就是要求用扭曲模拟旋转)。
    • 正解(2):再考虑基向量$vec b=(0,1)$。
      • 联立化简 $left{egin{matrix}egin{aligned}&x_1=x_0+y_0*tan(-frac heta2)\&y_1=y_0+x_1*tan(varphi)\&x_2=x_1+y_1*tan(-frac heta2)end{aligned}end{matrix} ight.$,$left{egin{matrix}egin{aligned}&x_0=0, y_0=1\&tan(- heta)=frac{x_2}{y_1}end{aligned}end{matrix} ight.$  得  $displaystylefrac{tan(frac heta2)[sec( heta)tan(varphi)-tan( heta)]}{tan(frac heta2)tan(varphi)-1}=0$
      • 解 $sec( heta)tan(varphi)=tan( heta)$ 得垂直扭曲角度 $varphi=tan^{-1}sin( heta)$
      • 啥?你还要$+kpi$?你还要分母不为$0$?$ heta$不为$0$?这都要我解,你咋不上天呢。
    • 发现题目对精度要求不高,反正切取个整即可。也可以直接打个垂直扭曲角度的表。
    • 更多请参考  Matrix67—线性代数的妙用:怎样在Windows画图软件中实现28度旋转?

    参考代码

    对基向量$vec b=(0,1)$的模拟:

     1 #include <stdio.h>
     2 #include <math.h>
     3 const double PI = acos(-1.L);
     4 int main() {
     5     double x = 0.0, y = 1.0;
     6     x = x + tan(-14.*PI/180.) * y;
     7     y = y + tan(25.*PI/180.) * x;///注意这里是tan25模拟sin28,若这里直接用sin28则最后atan回来的结果是28.0整
     8     x = x + tan(-14.*PI/180.) * y;
     9     printf("%f
    ", atan(x/y)*180./PI);
    10     return 0;
    11 }

    官方代码:

    1 // http://scarky.com/widget/getiframe/PLNRB5QG/
    2 #include <stdio.h>
    3 int y[]={0,2,4,6,8,10,12,14,15,17,19,21,22,24,25,27,28,29,30,32,33,34,35};
    4 int main(int i) {
    5   while(~scanf("%d", &i))
    6     i/=2, printf("%d %d %d
    ", -i, i>0?y[i]:-y[-i], -i);
    7   return 0;
    8 }

    非官方代码:

    1 #include <stdio.h>
    2 #include <math.h>
    3 const double PI = acos(-1.L);
    4 int main() {
    5     int x;
    6     while(~scanf("%d", &x))
    7         printf("%d %.f %d
    ", -x/2, round(atan(sin(x*PI/180.))*180./PI), -x/2);
    8     return 0;
    9 }

    ——原创by BlackStorm,转载请注明出处。

    本文地址:http://www.cnblogs.com/BlackStorm/p/5380872.html

  • 相关阅读:
    【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】
    【模拟题(63550802...)】解题报告【贪心】【拓扑排序】【找规律】【树相关】
    【模拟题(电子科大MaxKU)】解题报告【树形问题】【矩阵乘法】【快速幂】【数论】
    IMemoryBufferReference and IMemoryBufferByteAccess
    SoftwareBitmap and BitmapEncoder in Windows.Graphics.Imaging Namespace
    Windows UPnP APIs
    编译Android技术总结
    Windows函数转发器
    Two Ways in Delphi to Get IP Address on Android
    Delphi Call getifaddrs and freeifaddrs on Android
  • 原文地址:https://www.cnblogs.com/BlackStorm/p/5380872.html
Copyright © 2011-2022 走看看