NOIP 2012
- Prob.1 vigenere密码
模拟
代码:#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char K[105],A[1005];
int main(){
scanf("%s",K);int p=0;
scanf("%s",A);
for(int i=0;A[i];i++){
char k=K[p]; if(isupper(k)) k+=32;
char a=A[i]; if(isupper(a)) a+=32;
for(char c='a';c<='z';c++){
char aa=(c-'a'+k-'a') % 26+'a';
if(aa!=a) continue;
putchar(c-(isupper(A[i])?32:0)); break;
}
p++; if(!K[p]) p=0;
}
return 0;
} - Prob.2 国王游戏
贪心,按a*b(左右手权值积)从小到大排序
正确性证明(交换):
(排序后)考虑 相邻的两个大臣,
设第i个大臣的左右手权值分别为 a , b
设第i+1个大臣的左右手权值分别为 c , d
则 a*b<c*d
其他大臣得到的金币数不变,令第i个大臣前面的人的左手权值积为A
第i个大臣的金币数为 x1=A/b, 第i+1个大臣的金币数为 y1=A*a/d
如果交换i和i+1,则交换后
第i个大臣的金币数为 y1=A/d, 第i+1个大臣的金币数为 y2=A*c/b
易得 y2>y1>x1且y2>x1,所以交换后,最大值会变大。
所以不能交换。
然后就是一个高精度乘和低精度运算的事了。代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define bit 10000
using namespace std;
struct people{
int a,b;
bool operator <(const people &rtm) const{
return a*b<rtm.a*rtm.b;
}
}p[1005];
struct Big_int{//高精与低精的运算
int a[1005],len;
Big_int(){
memset(a,0,sizeof(a)); len=1;
}
void operator =(int rtm){
if(!rtm) return;
len=0; while(rtm){
a[++len]=rtm%bit;
rtm/=bit;
}
}
bool operator <(const Big_int &rtm) const {
if(len!=rtm.len) return len<rtm.len;
for(int i=len;i;i--)
if(a[i]!=rtm.a[i]) return a[i]<rtm.a[i];
return 0;
}
Big_int operator *(const int &rtm) const {
Big_int now; now.len=len+1;
for(int i=1;i<=len;i++){
now.a[i]+=a[i]*rtm;
now.a[i+1]+=now.a[i]/bit;
now.a[i]%=bit;
}
while(now.len>1&&!now.a[now.len]) now.len--;
return now;
}
Big_int operator /(const int &rtm){
Big_int now; now.len=len; int val=0;
for(int i=len;i;i--){
val=val*bit+a[i];
now.a[i]=val/rtm;
val%=rtm;
}
while(now.len>1&&!now.a[now.len]) now.len--;
return now;
}
void Print(){
printf("%d",a[len]);
for(int i=len-1;i;i--)
printf("%04d",a[i]);
printf("\n");
}
};
int main()
{
//freopen("in.in","r",stdin);
int n; scanf("%d",&n);
for(int i=0;i<=n;i++)
scanf("%d%d",&p[i].a,&p[i].b);
sort(p+1,p+n+1);
Big_int sumA,now,ans;
sumA=p[0].a;
for(int i=1;i<=n;i++){
now=sumA/p[i].b;
if(ans<now) ans=now;
sumA=sumA*p[i].a;
}
ans.Print();
return 0;
} - Prob.3 开车旅行
暴力的话是O(n2+nm),由于决策单一(即某人从某点出发到下一点这一过程是唯一确定的),可以进行倍增加速。
由于是两人交替走,比一般的路径倍增要麻烦一点
先借助set预处理出两个人分别从i号点向前走的下一个点是哪个以及走的距离。
然后用to[i][j]表示从i号点出发,走2j轮(一轮为小A先走,小B再走)到达的目的地。用dis[i][j][0/1](0:小B,1:小A)与上面的to数组对应,即分别表示从i号点出发,走2j轮,小B/小A走过的距离和。
这样通过倍增后,加速了答案的寻找过程,
将时间复杂度优化为了O(n log2n+m log2n)
更加详细的大佬题解-------------------------------------------------------->
代码:#include<set>
#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 0x3f3f3f3f
#define eps 0.000003
#define MAXN 100005
#define siter set<info>::iterator
#define info(a,b) (info){a,b}
using namespace std;//0 小B 1 小A
struct info{
int h,p;
bool operator <(const info rtm) const{
return h<rtm.h;
}
};
int to[MAXN][25],dis[MAXN][25][2],des[MAXN][2],len[MAXN][2],ans[2];
int he[MAXN];
int n,m,x,st;
set<info>s;
int sign(double i){
if(-eps<=i&&i<=eps) return 0;
if(i<-eps) return -1;
return 1;
}
int distant(int i,int j){
return he[i]>he[j]?he[i]-he[j]:he[j]-he[i];
}
void update(int i,info p){
int j=p.p,d=distant(i,j);
if(d<len[i][0]||(d==len[i][0]&&he[j]<he[des[i][0]])){
len[i][1]=len[i][0];des[i][1]=des[i][0];
len[i][0]=d;des[i][0]=j;
}
else if(d<len[i][1]||(d==len[i][1]&&he[j]<he[des[i][1]])){
len[i][1]=d;des[i][1]=j;
}
}
void drive(int i,int v){
for(int j=20;j>=0;j--) if(dis[i][j][0]+dis[i][j][1]<=v&&to[i][j]){
ans[0]+=dis[i][j][0];
ans[1]+=dis[i][j][1];
v=v-dis[i][j][0]-dis[i][j][1];
i=to[i][j];
}
if(len[i][1]<=v&&des[i][1]) ans[1]+=len[i][1];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&he[i]),len[i][0]=len[i][1]=INF;
siter si;
for(int i=n;i>=1;i--){
s.insert(info(he[i],i));
si=s.find(info(he[i],i));
si++;if(si!=s.end()){
update(i,*si);
si++; if(si!=s.end()) update(i,*si); si--;
}
si--;if(si!=s.begin()){
si--; update(i,*si);
if(si!=s.begin()) si--,update(i,*si);
}
}
for(int i=1;i<=n;i++)
to[i][0]=des[des[i][1]][0],
dis[i][0][1]=len[i][1],
dis[i][0][0]=len[des[i][1]][0];
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
to[i][j]=to[to[i][j-1]][j-1],
dis[i][j][0]=dis[i][j-1][0]+dis[to[i][j-1]][j-1][0],
dis[i][j][1]=dis[i][j-1][1]+dis[to[i][j-1]][j-1][1];
scanf("%d",&x);
double rat=1e9; int ap=0;
for(int i=1;i<=n;i++){
ans[0]=ans[1]=0;
drive(i,x);
double tmp=ans[0]? 1.0*ans[1]/ans[0]:1e9;
if(sign(rat-tmp)==0&&he[i]>he[ap]) ap=i;
if(sign(rat-tmp)>0) ap=i,rat=tmp;
}
printf("%d\n",ap);
scanf("%d",&m);
for(int i=1;i<=m;i++){
ans[0]=ans[1]=0;
scanf("%d%d",&st,&x);
drive(st,x);
printf("%d %d\n",ans[1],ans[0]);
}
return 0;
} - Prob.4 同余方程
(a,b互质辣)
得出线性方程 ax+(-by)=1(其实不用加那个"-")
拓展欧几里得求出一组解,然后把x调整到最小正整数。
代码:#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
void gcd(int a,int b,int &g,int &x,int &y){
if(!b){g=a; x=1; y=0;return;}
gcd(b,a%b,g,y,x); y-=x*(a/b);
}
int main(){
int a,b,x,y,g;
scanf("%d%d",&a,&b);
gcd(a,b,g,x,y);
b/=g;
if(x<0){int k=(0-x)/b+1;x+=k*b;}
if(x>0){int k=(x-0-1)/b;x-=k*b;}
printf("%d",x);
return 0;
} - Prob.5 借教室
1).线段树区间修改在线做,看什么时候区间最小值小于0即可。
2).线段树常数大(但可以过的),可以二分+差分判断做
代码:#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 1000006
using namespace std;
struct Application{
int d,l,r;
}t[MAXN];
int a[MAXN];
int n,m;
bool check(int p){
static int now,c[MAXN];
memset(c,0,sizeof(c)); now=0;
for(int i=1;i<=p;i++)
c[t[i].l-1]+=t[i].d,
c[t[i].r]-=t[i].d;
for(int i=0;i<=n;i++){
if(a[i]-now<0) return 0;
now+=c[i];
}
return 1;
}
int binary_search(){
int l=1,r=m,mid,now=0;
while(l<=r){
mid=(l+r)>>1;
if(check(mid)) now=mid,l=mid+1;
else r=mid-1;
}
return now;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d%d%d",&t[i].d,&t[i].l,&t[i].r);
int ans=binary_search();
if(ans+1>n) printf("0");
else printf("-1\n%d",ans+1);
return 0;
} - Prob.6 疫情控制
贪心+二分+倍增
二分时间,check操作,将所有军队按能否到达根节点分成两类:
A类:无法在二分的时间内达到根节点。
根据贪心策略,将这些军队移动到尽可能靠上的位置一定更优,所以把他们移动到他们所能到达的最靠近根的位置
B类:在二分的时间内可以到达根节点。
把他们放入一个数组,按到达根节点后剩余的时间从小到大排序。
再对树跑一个dfs,维护出根的哪些儿子节点还需要一个B类军队去驻扎,把这些儿子节点放入另一个数组,按到根的时间从小到大排序。
进行贪心,尝试用B类军队去覆盖没有还需要被驻扎的(根的儿子)节点:
对于从小到大枚举到的某一个B类军队,首先判断他到根节点时进过的那个根的儿子节点是否被驻扎,若没有,则直接去驻扎那个节点。若已被驻扎,则尝试去驻扎从小到大枚举到的还需要被驻扎的第一个节点。(有一点绕,好好理解一下,正确性很容易证明)
最后判断该时间下,那些还需要被驻扎的(根的儿子)节点是否被驻扎完。
至于倍增用在哪里,显而易见,在将军队向上移动时,不可能一个一个地向father移动,所以倍增一下,加速移动过程。代码:
洛谷和Vijos上过了,但Codevs和Tyvj上却WA了一个点,在Tyvj上把数据下了下来,手测却发现输出是正确的……
不明原因,非常绝望,望有心人能解答疑难。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define MAXN 50005
using namespace std;
struct edge{
int to,next;
ll val;
}e[MAXN*2];
struct node{
int id; ll val;
bool operator<(const node &rtm) const{
return val<rtm.val;
}
}ar[MAXN],ne[MAXN];
ll stt[MAXN][20];
int stu[MAXN][20];
int p[MAXN],from[MAXN],head[MAXN];
bool vis[MAXN];
ll l,r,mid,ans;
int n,m,ent=1,rs,cnt,nnt;
void add(int u,int v,int w){
e[ent]=(edge){v,head[u],1ll*w};
head[u]=ent++;
}
void dfs(int u,int fa,ll dis,int fr){
if(fa==1) rs++;
stu[u][0]=fa;
stt[u][0]=dis;
if(fa==1) from[u]=u;
else from[u]=fr;
for(int j=1;j<=16;j++){
stu[u][j]=stu[stu[u][j-1]][j-1];
stt[u][j]=stt[u][j-1]+stt[stu[u][j-1]][j-1];
}
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa) continue;
if(u==1) dfs(v,u,e[i].val,v);
else dfs(v,u,e[i].val,fr);
}
}
void update(int u,int fa){
bool fg=1,fl=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa) continue;
fl=1;
update(v,u);
if(!vis[v]) fg=0;
if(u==1&&!vis[v]) ne[++nnt]=(node){v,e[i].val};
}
if(fl) vis[u]=fg|vis[u];
}
bool check(ll x){
ll tmp;int u;
cnt=0; nnt=0;
memset(vis,0,sizeof(vis));vis[0]=1;
for(int i=1;i<=m;i++){
tmp=x; u=p[i];
for(int j=16;j>=0;j--)if(stu[u][j]&&tmp>=stt[u][j]){
tmp-=stt[u][j];
u=stu[u][j];
}
if(u==1) ar[++cnt]=(node){p[i],tmp};
else vis[u]=1;
}
update(1,0);
sort(ne+1,ne+nnt+1);
sort(ar+1,ar+cnt+1);
int pp=1,res=nnt;
for(int i=1;i<=cnt;i++){
while(vis[ne[pp].id]) pp++;
if(!vis[from[ar[i].id]]){
vis[from[ar[i].id]]=1;
res--;
}
else{
if(ar[i].val>=ne[pp].val){
vis[ne[pp].id]=1;
res--;
}
}
if(!res) return 1;
}
return 0;
}
void Binary(){
while(l<=r){
mid=(l+r)/2;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld",ans);
}
int main()
{
scanf("%d",&n);
l=1; r=0;
for(int i=1,a,b,c;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c); add(b,a,c);
r+=1ll*c;
}
dfs(1,0,0,0);
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d",&p[i]);
if(m<rs) printf("-1\n");
else Binary();
return 0;
}
NOIP 2012的更多相关文章
- NOIp 2012 #2 借教室 Label:区间修改线段树
题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然 ...
- NOIp 2012 #1 Vigenère 密码 Label:模拟
题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南 ...
- NOIP 2012 Day2T2 借教室题解
NOIP 2012 Day2T2 借教室题解 题目传送门:http://codevs.cn/problem/1217/ 题目描述 Description 在大学期间,经常需要租借教室.大到院系举办活动 ...
- NOIP 2012 T5 借教室 [洛谷P1083]
题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自 ...
- 【NOIP 2012 疫情控制】***
题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散 ...
- 【NOIP 2012 开车旅行】***
题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的 城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 Hi,城市 ...
- 【NOIP 2012 国王游戏】 贪心+高精度
题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 成一排,国王站在队伍 ...
- NOIP 2012 Vigenère 密码
洛谷 P1079 Vigenère 密码 https://www.luogu.org/problemnew/show/P1079 JDOJ 1779: [NOIP2012]Vigenèr密码 D1 T ...
- P1080 【NOIP 2012】 国王游戏[贪心+高精度]
题目来源:洛谷 题目描述 恰逢 H国国庆,国王邀请n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王 ...
- NOIP 2012 洛谷P1081 开车旅行
Description: 就是两个人开车,只能向东开.向东有n个城市,城市之间的距离为他们的高度差.A,B轮流开车,A喜欢到次近的城市,B喜欢到最近的城市.如果车子开到底了或者车子开的路程已经超过了限 ...
随机推荐
- 高级软件工程2017第6次作业--团队项目:Alpha阶段综合报告
高级软件工程2017第6次作业--团队项目:Alpha阶段综合报告 Deadline:2017-10-30(周一)21:00pm (注:以下内容参考集大作业4,集大作业5,集大作业6,集大作业7 一. ...
- Alpha冲刺Day9
Alpha冲刺Day9 一:站立式会议 今日安排: 经过为期5天的冲刺,基本完成企业人员模块的开发.因第三方机构与企业存在委托的关系.第三方人员对于风险的自查.风险列表的展示以及自查风险的统计展示(包 ...
- 我所知道的window.location
多说无益 直接上干货 假如一个地址为 http://127.0.0.1:5000/index.html?id=4 window.location.href -- 完整路径 -- http://127 ...
- 在网络编程中的io流小问题
在客户端和服务端调用io流进行传输数据的过程中,当将数据write到outputstream中,需要及时刷新,否则会发生io阻塞. 在输入数据的时候,最好选用BufferedReader,因为read ...
- JS页面跳转的常用方法整理.
<script type="text/javascript"> //js页面跳转 function showtabs() { window.location.href ...
- JAVA_SE基础——17.方法的重载
方法重载: 方法重载就是方法名称重复,加载参数不同. 具体规范: 一.方法名一定要相同. 二.方法的参数表必须不同,包括参数的类型或个数,以此区分不同的方法体. 1.如果参数个数不同,就不管它的参数类 ...
- 安装CentOS7,连接mysql提示密码错误
1.grep 'temporary password' /var/log/mysqld.log 如果上面命令没有查看到密码 2.修改my.cnf文件.在mysqld下加入skip-grant-tabl ...
- DDD实战进阶第一波(二):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架一)
要实现软件设计.软件开发在一个统一的思想.统一的节奏下进行,就应该有一个轻量级的框架对开发过程与代码编写做一定的约束. 虽然DDD是一个软件开发的方法,而不是具体的技术或框架,但拥有一个轻量级的框架仍 ...
- crontab的mysqldump备份任务未能完全正确执行的故障处理
crontab是每个运维一线人员必须掌握的技术,熟练运用crontab可以自动帮助我们执行重复性的工作,提高运维的工作效率.它就像一个闹钟,在特定的时间,准时响应并执行相应的任务.如果你的工作经常与L ...
- 115 个 Java 面试题和答案——终极(上)
目录 面向对象编程(OOP)常见的 Java 问题Java 线程Java 集合类垃圾收集器 面向对象编程(OOP) Java 是一个支持并发.基于类和面向对象的计算机编程语言.下面列出了面向对象软件开 ...