[noip模拟赛2017.7.16]
题目名称 | 黑魔法师之门 | 守卫者的挑战 | 终极武器
---|---|---|---|---
程序文件名 | magician | guard | laser
输入文件名 | magician.in | guard.in | laser.in
输出文件名 | magician.out | guard.out | laser.out
每个测试点时限 | 1 秒 | 1 秒 | 2 秒
内存限制 | 128 MB | 128 MB | 128 MB
测试点数目 | 10 | 10 | 20
每个测试点分值 | 10 | 10 | 5
是否有部分分 | 无 | 无 | 无
[Nescafe17]是石家庄二中 NOIP 校内测试,机房评测环境: Intel Pentium Dual-Core E5300 @2.66GHz 2.66GHz, 2.00GB RAM Cena 0.8.2 @ Windows 7 Ultimate x86 SP1 C++选手注意 Windows 7 及以上版本可以使用 %lld 输入输出 64 位整数。
黑魔法师之门 (magician.pas/c/cpp)
题目描述
经过了 16 个工作日的紧张忙碌,未来的人类终于收集到了足够的能源。然而在与 Violet 星球的战争中,由于 Z 副官的愚蠢,地球的领袖 applepi 被邪恶的黑魔法师 Vani 囚禁在了 Violet 星球。为了重启这一宏伟的科技工程,人类派出了一支由 XLk、Poet_shy 和 lydrainbowcat 三人组成的精英队伍,穿越时空隧道,去往 Violet 星球拯救领袖 applepi。 applepi 被囚禁的地点只有一扇门,当地人称它为“黑魔法师之门”。这扇门上画着一张
无向无权图,而打开这扇门的密码就是图中每个点的度数大于零且都是偶数的子图的个数 对 1000000009 取模的值。此处子图 (V, E) 定义为:点集 V和边集 E 都是原图的任意子集, 其中 E 中的边的端点都在 V中。 但是 Vani 认为这样的密码过于简单,因此门上的图是动态的。起初图中只有 N 个顶点 而没有边。Vani 建造的门控系统共操作 M 次,每次往图中添加一条边。你必须在每次操作 后都填写正确的密码,才能够打开黑魔法师的牢狱,去拯救伟大的领袖 applepi。
输入格式
第一行包含两个整数 N 和 M。 接下来 M 行,每行两个整数 A和 B,代表门控系统添加了一条无向边(A,B)。
输出格式
输出一共 M 行,表示每次操作后的密码。
样例输入
4 8
3 1
3 2
2 1
2 1
1 3
1 4
2 4
2 3
样例输出
0
0
1
3
7
7
15
31
样例说明
第三次添加之后,存在一个满足条件的子图 {1, 2, 3}(其中 1, 2, 3 是数据中边的标号)。
第四次添加之后,存在三个子图 {1, 2, 3},{1, 2, 4},{3, 4}。 „„
数据范围与约定
对于 30% 的数据,N, M≤10。 对于 100% 的数据,N≤200000,M≤300000。
题解
比较神奇的一个题,本来先xjb算法建图,然后两个加强版并查集弄来弄去......然后找到的此题的命脉,搞半天只要一个最普通的并查集,如果当前要加边的两个点早就在一个集合,ans=ans*2+1,不然直接加入同一个集合即可.
#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
const LL mod=1000000009;
int fa[200200];
int n,m;
LL ans;
void pre(){
for(int i=1;i<=n;i++)
fa[i]=i;
}
int findf(int x){
return x==fa[x]?x:fa[x]=findf(fa[x]);
}
void mergef(int x,int y){
fa[findf(x)]=findf(y);
}
int main(){
freopen("magician.in","r",stdin);
freopen("magician.out","w",stdout);
scanf("%d%d",&n,&m);
pre();
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
if(findf(x)!=findf(y))mergef(x,y);
else ans=((ans+1)*2%mod-1+mod)%mod;
printf("%lld\n",ans);
}
}
/*
4 8
3 1
3 2
2 1
2 1
1 3
1 4
2 4
2 3
5 7
1 2
1 3
2 3
4 5
5 4
2 4
4 2
7 10
1 2
2 3
3 1
4 5
5 4
6 7
7 6
3 4
3 6
6 4
*/
守卫者的挑战 (guard.pas/c/cpp)
题目描述
打开了黑魔法师 Vani 的大门,队员们在迷宫般的路上漫无目的地搜寻着关押 applepi 的 监狱的所在地。突然,眼前一道亮光闪过。“我,Nizem,是黑魔法圣殿的守卫者。如果你
能通过我的挑战,那么你可以带走黑魔法圣殿的地图„„”瞬间,队员们被传送到了一个擂 台上,最初身边有一个容量为K 的包包。
擂台赛一共有 N 项挑战,各项挑战依次进行。第i 项挑战有一个属性 i a ,如果 ia ,
表示这次挑战成功后可以再获得一个容量为 i a 的包包;如果 ia ,则表示这次挑战成功
后可以得到一个大小为 1 的地图残片。地图残片必须装在包包里才能带出擂台,包包没有
必要全部装满,但是队员们必须把获得的所有的地图残片都带走(没有得到的不用考虑,只 需要完成所有 N 项挑战后背包容量足够容纳地图残片即可),才能拼出完整的地图。并且他 们至少要挑战成功L次才能离开擂台。 队员们一筹莫展之时,善良的守卫者 Nizem 帮忙预估出了每项挑战成功的概率,其中
第i项挑战成功的概率为
ip 。现在,请你帮忙预测一下,队员们能够带上他们获得的地图
残片离开擂台的概率。
输入格式
第一行三个整数 N , L, K 。 第二行 N 个实数,第i个实数 i p 表示第i项挑战成功的百分比。
第三行 N 个整数,第i个整数 i a 表示第i项挑战的属性值.
输出格式
一个整数,表示所求概率,四舍五入保留 6 位小数。
样例输入1
3 1 0
10 20 30
-1 -1 2
样例输出1
0.300000
样例输入2
5 1 2
36 44 13 83 63
-1 2 -1 2 1
样例输出2
0.980387
样例说明
在第一个样例中,若第三项挑战成功,如果前两场中某场胜利,队员们就有空间来容纳
得到的地图残片,如果挑战失败,根本就没有获得地图残片,不用考虑是否能装下;若第三
项挑战失败,如果前两场有胜利,没有包来装地图残片,如果前两场都失败,不满足至少挑战成功L次的要求。因此所求概率就是第三场挑战获胜的概率。
数据范围与约定
对于 100% 的数据,保证 K<=2000,N<=200, -1<=a[i]<=1000 , 0<L<=N ,
p[i]<=100 。
题解
经典概率DP啊,考虑最多获得200块地图碎片,也就是说,我们最多只需要200容量的背包,其他背包多了都用不上的,所以,我们不妨设背包上限为200,下限为-200(即没有包包,却有200块地图),然后开三维空间DP,dp[i][j][k]表示第i天胜利j场还有k容量的包包的概率
kk=min(k+a[i],400)
dp[i+1][j+1][kk]+=dp[i][j][k]*p[i+1]
dp[i+1][j][k]+=dp[i][j][k]*(1-p[i+1])
由于数组下标只有大于0的,我们就把k全部加上200,所以其范围为[0,400],
然后根据以上的转移方程,我们就能很方便地进行动态规划了。
#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
double p[1000];
int a[1000];
int N,L,K;
double f[250][250][500];
double ans;
int main(){
freopen("guard.in","r",stdin);
freopen("guard.out","w",stdout);
scanf("%d%d%d",&N,&L,&K);
for(int i=1;i<=N;i++)
scanf("%lf",&p[i]),p[i]/=100;
for(int i=1;i<=N;i++)
scanf("%d",&a[i]);
p[++N]=1;L++;a[N]=K;
f[0][0][200]=1;
for(int i=0;i<=N;i++)
for(int j=0;j<=i;j++)
for(int k=0;k<=500;k++){
int kk=min(a[i+1]+k,450);
f[i+1][j][k]+=f[i][j][k]*(1-p[i+1]);
f[i+1][j+1][kk]+=f[i][j][k]*p[i+1];
}
for(int i=L;i<=N;i++)
for(int j=200;j<=450;j++)
ans+=f[N][i][j];
printf("%.6lf",ans);
}
/*
3 1 0
10 20 30
-1 -1 2
*/
终极武器 (laser.pas/c/cpp)
题目描述
经过一番周折,精英队伍的队员们终于来到了关押 applepi 的牢狱面前。心中神一般的 领袖 applepi 就在眼前,队员们都不由自主地跪烂膝盖„„不过令他们沮丧的是,牢狱的大 锁没有钥匙孔,黑魔法师 Vani 根本就没有指望它再被打开。幸好队员们携带了新研制的终 极武器——k 型氙激光器(Xenon Laser - k,代号XLk),可以用来破拆这把锁。不过作为一 道终极武器,它的启用规则异常严格。
Xenon Laser - k 上共有N 个波段能够发射激光,每个波段可以用一个闭区间 ] [ i i,ba 来表
示,其中 i i,ba 为正整数, i ii b ab 。对于两个数字 p 和 q ,如果对于这N 个波段内的
任意一个整数num,把它在十进制表示下的后k 位中某一位上的 p 换成q(或者q 换成 p ),
都满足得到的整数仍然在这 N 个波段内,那么称在该激光器中,数字 p 和q 是k 等价的。
我们称两两之间k 等价的数字组成一个k 等价类。 激光器附带了 9 个发射匣,代表 1~9 这 9 个数字。只有把同一个等价类的数字对应的发 射匣安置在一排上,Xenon Laser - k 才能够启动。给定N 个波段,现在就请你求出 1~9 这 9 个数字分成了哪些等价类,并且每行输出一个等价类。 本题描述比较抽象,请参考样例解释。
输入格式
第一行两个整数 N ,k 。 接下来 N 行每行两个整数 a[i],b[i] 为正整数,满足b[i-1]<a[i]<=b[i]。
输出格式
每行一个k 等价类,各行之内都按照数字从小到大排序,数字中间没有空格,行与行之 间按照等价类中最小的数字从小到大排序。具体格式参考样例。
样例输入1
1 1
1 566
样例输出1
123456
789
样例输入2
1 2
30 75
样例输出2
12
345
6
7
89
样例说明
第一个样例中 1 k ,只允许修改个位。对于 1~559 这些数,个位无论如何修改都在波 段内。对于 560~566 这些数,个位修改为大于等于 7 的数字时(例如 562 的 2 修改为8), 就不在波段内了。因此 1~6 和 7~9 属于不同的等价类。 第二个样例每一位上都可以修改。修改方法与上面一个样例类似。
数据范围与约定
对于 25% 的数据,n<=50 , a<b<=6000 。
对于另 25% 的数据,n=1 。
对于另 30% 的数据, k=1 。
对于 100% 的数据,n<=10000 ,k<=19 ,a[i]<=b[i]<=1e18 。
在所有的数据中,均匀分布着 25% 的随机数据。
题解
玄学混分,直接输出1,2,3,4,5,6,7,8,9,因为25%地随机数据,我们可以容易想到,这就个数字是很容易分开的,那么这25分就,至于其他的分。。。看运气吧
附上65分代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
const LL INF=9223372036854775807;
int n,k;
LL a[10010],b[10010];
LL ksm[]={1,1e1,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18};
bool com[20][20];
bool deal(LL num){
int l=0,r=n+1,p1,p2;
while(l<r){
int mid=(l+r)/2+1;
if(a[mid]>num)r=mid-1;
else l=mid;
}
p1=l;
l=0,r=n+1;
while(l<r){
int mid=(l+r)/2;
if(a[mid]<num)l=mid+1;
else r=mid;
}
p2=l;
if(p1!=p2)return false;
return true;
}
void check(int pos,int id){
LL x1=a[id]/ksm[pos]%10;
LL x2=b[id]/ksm[pos]%10;
for(int i=0;i<x1;i++){
LL num=a[id]-(x1-i)*ksm[pos];
bool rst=deal(num);
if(rst==false){
for(int j=x1;j<=x2;j++)
com[i][j]=false,com[j][i]=false;
}
}
for(int i=x2+1;i<=9;i++){
LL num=b[id]+(i-x2)*ksm[pos];
bool rst=deal(num);
if(rst==false){
for(int j=x1;j<=x2;j++)
com[i][j]=false,com[j][i]=false;
}
}
if(b[id]%ksm[pos]>0){
LL num=(b[id]-b[id]%ksm[pos])+ksm[pos]-1;
bool rst=deal(num);
if(rst==false){
for(int j=x1;j<=x2-1;j++)
com[x2][j]=false,com[j][x2]=false;
}
}
}
bool flag[20];
int main(){
freopen("laser.in","r",stdin);
freopen("laser.out","w",stdout);
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
com[i][j]=true;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&a[i],&b[i]);
a[0]=0;b[0]=0;
a[n+1]=INF;b[n+1]=INF;
for(int p=0;p<=k-1;p++)
for(int w=1;w<=n;w++)
check(p,w);
for(int i=1;i<=9;i++){
if(!flag[i]){
for(int j=1;j<=9;j++){
if(com[i][j])
printf("%d",j),flag[j]=true;
}
printf("\n");
}
}
}
/*
1 1
1 566
1 2
30 75
*/