HZ集训日 day1:距离CCF[noip2015仅31days]
番外篇:
话说今天刚考完试,考试成绩就发了下来,不过考完试我们就去集训了,先来公布小蒟蒻[蒟蒻:一种植物..后来被人们形容成比较弱的人]的考试成绩吧,语文106[话说这次语文脱坑也是意料之中],数学131[QAQ第11题排除只剩下CD还选错了],英语124[...不作任何评价,看着悲催的二卷我无语了],物理98[涂错俩选择应该106...],化学84[不知是否要补化学作业,那么难做的选择就错俩才考了84,真是个悲剧,原谅我自嘲一下],话说集训日第一天物理老师趁我不在,又提起我的成绩。。。。很无语
生物[选择还是硬,扣一分,又到了烦人的二卷了...扣扣扣扣....直到75分满分90..]总分618超过了曾经年级190-的LGL..;
一句话结束 革命尚未成功,同志仍需努力
好了..转入正题
先写一下昨天考试的酱油记吧
Day1 80 Day2 70还算是给自己了一个交代:
先来写一下day1吧
找伙伴(partner)
在班级里,每个人都想找学习伙伴。
伙伴不能随便找,都是老师固定好的,老师给出要求:伙伴要凭自己实力去找。
老师给每个人发一张纸,上面有数字。假设你的数字是w,那么如果某位同学手中的数字的所有正约数之和等于w,那么这位同学就是你的小伙伴。
Input
输入包含n组数据(最多100组)
对于每组测试数据,输入只有一个数字w。
Output
对于每组数据输出两行,第一行包含一个整数m,表示有m个伙伴,第二行包含相应的m个数,表示伙伴的数字。注意:小伙伴的数字必须按照升序排列。
SampleInput
42
SampleOutput
3
2026 41
HINT
100%的数据,有w<=2*10^9
时间限制:1s
我是数学蒟蒻,一遇见数学果断打暴力,这个题因为没有小数据,得了0分
我先不写这个题的题解
若急需题解请参考
http://blog.csdn.net/lglin000/article/details/48876191;
第 2题 我们爱几何 (geometry)
AngryBacon 和 ftiasch 是好朋友。ftiasch 酷爱几何,于是 AngryBacon 经常会为 ftiasch 出一些几何 题。这次,他又出了一道题目:
给出平面上的 N 个整点,AngryBacon 想选取这 N 个点中的 K 个点,连成一个正 K 边形。 正 K 边形的定义为边数为 K 的多边形,要求 K >=3,各边相等,各角也相等。 AngryBacon 想知道他最多能取多少个点。
输入格式
第一行,1 个整数 T , 表示数据组数。接下来是T 组数据。 每组数据的第一行包含一个整数 N,表示给定的点数。 接下来的 N 行每行两个整数,分别表示一个点的 x; y 坐标。
输出格式
对于每组数据,输出一行,包含一个整数,表示算最多可取的点数。如果无法连成任何的合法正多边 形,则输出 1。
输入样例
2
5
1 0
0 1
1 2
2 1
1 1
4
0 1
1 2
2 1
1 1
输出
4
-1
Solution:
对于n<=10,只要暴力枚举所有排列就好了。
对于n<=20,稍有基础的同学应该能想到一个O(n*n*2^n) 的 DP,即f[s][i][j]表示前i个位置填完了,用了状态为s的人(0<=s<2^n),其中j个人感到开心的方案数。 使用滚动数组滚动i这一维可以做到空间复杂度O(2^n*n)。 这时候你可能觉得这个时间复杂度对于n=20有些太大(事实上在评测机上只用0.5秒即可出解),但此时应该注意到输入的n、k只有20*20=400种,完全可以在本地把答案算好提前写到源程序里去。 这个常见的技巧叫做打表,如果你以前没有听说过,现在可以把它加入骗分技巧大全中。
对于n=1000,是本试中最难的问题,难度稍高于noip一等。需要掌握一点基本的容斥原理,我尽量解释的详细些,实在不懂去理解程序。 因为给定的是环,首先枚举前两位填的人是不是一定开心(如果枚举为开心的话继续枚举具体哪个人坐在上面),这样断环为链了,枚举量大概为14。 接下来f[i][j][mask]表示前i个位置中其中j个位置上的人必须开心(剩下的i-j个人先不考虑,最后可能开心也可能不开心),并且i+1和i+2这两个人是否已经入座(用0..3的mask表示)的方案数。转移则枚举坐在i+1这个位置的人是否开心,如果开心枚举谁坐在上面,转移到f[i+1][j+1][mask']。如果不开心则转移到f[i+1][j][mask'']。 dp到最后,和开头枚举的断环为链的状态不冲突即可视作合法。接下来对所有合法的状态进行容斥。
首先看f[n][n]这个状态,它的意义是所有位置上的人都开心的方案数。我们倒序枚举i,依次算出ans[i]为恰好i个人开心的方案数,可知ans[n]=f[n][n]。 我们假设已经算出了ans[k+1]到ans[n],现在要计算ans[k]。 再看f[n][k]这个状态,它的意义是n个位置里先选择了k个位置强制其开心,再为其选择谁坐在上面的方案数。有n-k个人没有考虑,我们不如假设其随便坐ans[k]=f[n][k]*(n-k)!。注意到这个时候ans[k]的答案是不对的,有些大于k个人开心的情况被重复计数了,于是要容斥。我们枚举大于k的已经计算好的ans[l],考虑ans[l]在ans[k]里被计数了多少次,可以发现被计数了C(l,k)次,于是ans[k] = ans[k] - ans[l] * C(l,k)。这样我们就利用容斥算出了ans[k]。
将多个初始状态的ans累加即得到答案。复杂度O(n^2)。
#include<cstdio>
#include<algorithm>
using std::sort;
const int MAXN=1010UL;
int n,T;
class node{public:int i,j;}a[MAXN];
inline int in(){char c=getchar();int x=0;while(c<'0'||c>'9')c=getchar();
for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';return x;}
inline int comp(const node &a,const node &b){return a.i==b.i?(a.j<b.j):a.i<b.i;}//排序可以搜索
inline bool check(int x,int y){
int l=1,r=n,m=(l+r)>>1;//二分思想。。。枚举点 看是否成立 成立 4 不成立 -1
if((a[m].i==x)and(a[m].j==y)) return true;
while(l<r){
if((a[m].i<x)or(a[m].i==x&&a[m].j<y)) l=m+1; else r=m-1;
m=(l+r)>>1;
if((a[m].i==x)and(a[m].j==y)) return true;
}return false;
}
inline bool g(){
sort(a+1,a+n+1,comp);
for(int i=1;i<=n;i++)
for(int j=i+1,x,y;j<=n;j++){
x=a[i].i-a[j].i,y=a[i].j-a[j].j;//上下左右搜索;
if((check(a[j].i-y,x+a[j].j))and(check(a[i].i-y,x+a[i].j))) return true;
x=-1*x,y=-1*y;
if((check(y+a[j].i,a[j].j-x))and(check(y+a[i].i,a[i].j-x))) return true;
}
return false;
}
int main(){
T=in();
while(T){T--,n=in();
for(int i=1;i<=n;i++)scanf("%d%d",&a[i].i,&a[i].j);
if(g()) putchar('4');else putchar('-'),putchar('1');
putchar('\n');
}return 0;
}
后记 本题是我唯一一道考场上没有打代码的题,代码是一个一个字母挤出来的...很不容易 向所有努力工作的HZOIer致敬!
第三题 我们爱序列 (sequence)
AngryBacon 非常喜欢序列,与序列有关的一切都喜欢。
AngryBacon 面前摆着一个长度为 N 的序列,每个元素为不超过 M 的正整数。
AngryBacon 会使用 Q 次魔法,每次魔法的内容为一对不超过 M 的正整数 a; b,表示将序列中所有 为 a 的数改写为 b。
AngryBacon 想知道在最后他心爱的序列变成了什么样。
输入格式
第一行,包含三个整数 N; M; Q,意义如上所述。
第二行,包含 N 个整数 A1; A2; : : : ; AN ,表示初始序列。 接下来的 Q 行,每行两个整数 a; b,意义如上所述。
输出格式
输出一行,包含 N 个整数,表示最后序列的形态
样例输入
5 5 3
1 2 34 5
3 1
4 3
1 5
样例输出
5 2 53 5
我这道题并没有想出正解,而是比别人多想了一下别人都用的O(n*q)效率的算法而我是用的O(n*m)的效率于是就得了60分 不过还是没有YMX和LGL高...
蒟蒻终究是蒟蒻啊..[话说LGL这道题用tarjan的思想得了100分..膜拜啊]
Solution
耐心想想,其实就是一个Union-Set[并查集],尽管考场上想到题目具有并查集的性质,但是这个并查集实在是不好打也不好想
对于n,q<=1000,暴力模拟不多讲。
对于m<=20,有一个非常显然的O(m*q+n)的做法,对每种数字c,我们记录color[c]表示初始数字为c的位置现在变成了什么数字,每次修改(a,b)只要枚举所有的m种数字,将所有color为a的改为b即可。 对于n,m,q<=1000000,则是一个非常正统的基础数据结构应用。我们使用并查集,n个位置每个位置都建立一个节点,m种颜色也都建立一个节点。对于初始的A[x]=y,我们将位置x的节点的父亲设为颜色y的节点。 对于一次修改(a,b),我们将颜色a的节点的父亲设为颜色b的节点,并对颜色a新建一个单节点。对于最后的输出,对于每个位置只要求出它所在的并查集的根是代表的哪个颜色即可。 注意对于a=b的情况的处理。
//上代码!Rank1
#include<cstdio>
#define MAXN 5000100ul
int n,m,q,fa[MAXN];
int color[MAXN],pos[MAXN],tot;
inline int find(int x){if(fa[x]==x)return x;fa[x]=find(fa[x]);return fa[x];}
inline int in(){
char c=getchar();int x=0;
while(c<'0'||c>'9')c=getchar();for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
return x;
}
int main(){
// freopen("sequence.in","r",stdin),freopen("sequence.out","w",stdout);
n=in(),m=in(),q=in(),tot=n+m;
for(int i=1;i<=m;i++)
fa[i]=color[i]=pos[i]=i;
for(int i=1,x;i<=n;i++)
x=in(),fa[m+i]=x;
for(int i=1,x,y;i<=q;i++){
x=in(),y=in();
if(x^y)
fa[color[x]]=color[y],color[x]=++tot,pos[color[x]]=x,fa[color[x]]=color[x];
}
for(int i=m+1;i<=n+m-1;i++) printf("%d ",pos[find(i)]);
printf("%d",pos[find(n+m)]);
return 0;
}
时间紧任务重,今天本来想总结day2的考试题,LHB留的任务太重了,不过因为优先要完成师傅的任务...所以今天只能停滞在day1了
其实这两次考试,大家都拉不开差距,为什么大家的得分都那么高呢,还是比谁敢于想,善于去挑战思维,也许这就是奥赛给我们带来的最大的benefit吧.=
每日一语:自己选择的路,即使跪着,也要走完.