码迷,mamicode.com
首页 > 其他好文 > 详细

2016 SCNUCPC 校赛非官方题解

时间:2016-04-12 19:13:19      阅读:351      评论:0      收藏:0      [点我收藏+]

标签:

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

 

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

 

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\n",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\n", 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\times N_1$个单位,能拿走最后一段纸带的人获胜,问Oyk第一次获胜是第几轮。

基本思路

  官方题解:技术分享

  非官方思路:不考虑限制条件,当$n=1$先手必胜。考虑$n=2$,Z大师只能拿一个,此时剩下的$n=2-1=1$个对Oyk来说是先手,Oyk胜。同理$n=3$,Z大师拿$1$/$2$个,剩下$n=2$/$n=1$对Oyk必胜。$n=4$时由于Z大师足够聪明,他只拿$1$个(这种情况拿$2$个就输了),即总能让前面后手赢的情况让给Oyk先手拿走,除掉Oyk拿走的纸带,剩下总的来说是后手必胜态,即两个各自拿走的总数相对来说Z大师是后手,且后手总能构造出前面出现过的后手必胜态,于是Z大师必胜。$n=5$甚至更大时,不管每轮拿多少,Oyk都可以尝试让Z大师先手拿走的数目是后手必胜,而剩下被Oyk后手拿走的数目也是后手必胜,这样Oyk就赢了。所以可以递推$a_1=1$,$a_2=2$,$a_n=a_{n-1}+a_{n-2}$,其中$a_n$是Oyk后手胜的纸条长度。

  汇总:无论如何这就是个斐波那契博弈,接下来我们要做的就是判断$K_i$是否为Fibonacci数。容易想到的是用递推打一个表,将Fibonacci数存起来或标记一下。但是我们知道斐波那契数列通项公式为$F_n=\frac1{\sqrt5}\left[\left(\frac{1+\sqrt5}2\right)^n-\left(\frac{1-\sqrt5}2\right)^n\right]$(比内公式),还知道判断一个数$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\n",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\n", res):puts("Oyk forever!");
25     }
26     return 0;
27 }
C. Oyk forever!

 

D. 最小费用流

 

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\n", 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+高斯消元

 

G. 逆元预处理组合数

 

H. 状态压缩DP

 

I. Square

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

基本思路:不考虑限制条件,有$2^{N^2}$种方案对吧?能乱填对吧?那如何保证每行每列中$1$的个数是奇数个?在旁边加多一行加多一列(即$(N+1)\times(N+1)$),对于每一行每一列,如果$1$的个数是偶数个,再填个$1$,否则填$0$进去。啥?剩下那个格子怎么办?会一边奇数一边偶数?嗯,由于是$N\times 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\n",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\n", 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 a=(0,1)$,然后想$x$轴扭曲$-14^\circ$应该是$\vec{a‘}=(tan14^\circ,1)$,$y$轴再扭曲$25^\circ$应该是$\vec{a‘‘}=(tan14^\circ, 1-tan25^\circ)$,再扭曲一下……再$arc tan$一下……咦?怎么出来的不是$28^\circ$了?

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

  正解:考虑向量$\vec x=\begin{bmatrix}x\\y\end{bmatrix}$,水平扭曲矩阵$A=\begin{bmatrix}1&tan(-\frac\theta2)\\0&1\end{bmatrix}$, ($A\vec x=\begin{bmatrix}x+ytan(-\frac\theta2)\\y\end{bmatrix}$,看不懂的学线代去,当然直接算三角函数没问题,矩阵好看点)

    三个扭曲矩阵相乘应该是$M=ABA$,其中我们要求的是中间的垂直扭曲矩阵$B$。

    已知旋转矩阵$M=\begin{bmatrix}cos\theta&-sin\theta\\sin\theta&cos\theta\end{bmatrix}$,解得$B=\begin{bmatrix}1&0\\sin\theta&1\end{bmatrix}$。

    但是我们的垂直扭曲矩阵应该要长成$B=\begin{bmatrix}1&0\\tan\varphi&1\end{bmatrix}$的样子。

    所以我们需要用$tan$正切值去模拟$sin$正弦值(本来就是要求用扭曲模拟旋转)。发现题目对精度要求不高,反正切取个整即可。也可以直接打个垂直扭曲角度的表。

参考代码

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

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

官方代码:

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\n", -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\n", -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

2016 SCNUCPC 校赛非官方题解

标签:

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!