- 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
时间: 2024-11-02 22:35:49
NOIP 2012的相关文章
NOIP 2012 提高组 DAY1 T2 国王游戏
题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 成一排,国王站在队伍的最前面.排好队后,所有的大臣都会获得国王奖赏的若干金币,每 位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右 手上的数,然后向下取整得到的结果. 国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序, 使得获得奖赏最多的大臣,所获奖赏尽可能的少
NOIP 2012 题解
[D1T1vigenere密码] P1778vigenere密码 Accepted 标签:[显示标签] 描述 16世纪法国外交家Blaise de Vigenère设计了一种多表密码加密算法--Vigenère密码.Vigenère密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用M表示:称加密后的信息为密文,用C表示:而密钥是一种参数,是将明文转换为密文或将密文转换为明文的算法中输入的数据,记为k. 在Vigenère
【NOIP 2012 国王游戏】 贪心+高精度
题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 成一排,国王站在队伍的最前面.排好队后,所有的大臣都会获得国王奖赏的若干金币,每 位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右 手上的数,然后向下取整得到的结果. 国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序, 使得获得奖赏最多的大臣,所获奖赏尽可能的少
扩展欧几里得模板(洛谷1082 同余方程NOIP 2012 提高组 第二天 第一题)
题目描述 求关于 x 的同余方程 ax ≡ 1 (mod b)的最小正整数解. 输入输出格式 输入格式: 输入只有一行,包含两个正整数 a, b,用一个空格隔开. 输出格式: 输出只有一行,包含一个正整数 x0,即最小正整数解.输入数据保证一定有解. 输入输出样例 输入样例#1: 3 10 输出样例#1: 7 说明 [数据范围] 对于 40%的数据,2 ≤b≤ 1,000: 对于 60%的数据,2 ≤b≤ 50,000,000: 对于 100%的数据,2 ≤a, b≤ 2,000,000,000
NOIP 2012 Day2
tags: 扩展欧几里得 二分答案 查分 倍增 二分答案 贪心 NOIP categories: 信息学竞赛 总结 同余方程 借教室 疫情控制 同余方程 Solution 首先同余式可以转化为等式. \[ax\equiv 1\mod b\Leftrightarrow ax+by=1\] 根据扩展欧几里得定理, 对于式 \[ax+by=k(a,b),k\in \mathbf{R}\]一定存在整数解.然而题面说一定存在解, 也就是说\((a,b)=1\), 然后就可以利用扩展欧几里得递归求得一组解.
NOIP 2012 Day1
tags: NOIP 模拟 倍增 高精 Python categories: 信息学竞赛 总结 Luogu P1079 Vigenère 密码 Solution 表示并不是很懂其他人发的题解. 我是这么想的, 既然是题目要求用密文转明文而且转换规则一定的, 所以就可以用明文转密文的逆过程来完成. 首先要搞明白明文是怎么变成密文的, 通过这个表可以观察到, 如果明文的一个字符是 ch1 ,密钥为 ch2 , 那么密文 ch3 对应的就是 ch1 在字母表中偏移 |ch2| 位, 例如 \(\tex
NOIP 2012 第一试 模拟题 魔法树 solution
题意 Solution 压位+前缀和 1 #include <cstdio> 2 #include <iostream> 3 #include <cmath> 4 #include <algorithm> 5 #define ll int 6 using namespace std; 7 const ll mod=100000007; 8 inline void read(ll &k) 9 { 10 ll f=1;char c=getchar();k
NOIP 2012 提高组第二试模拟赛 Solution
第一题 题意 数据范围 Solution 三分求下凹函数最值 1 #include <cstdio> 2 #include <queue> 3 #include <iostream> 4 using namespace std; 5 inline void read(int &k) 6 { 7 k=0;int f=1;char c=getchar(); 8 while (c<'0'||c>'9')c=='-'&&(f=-1),c=ge
NOIP 2012 Day1 T3 开车旅行
1 #include<cstdio> 2 #include<iostream> 3 #include<iomanip> 4 #include<cmath> 5 #include<vector> 6 #include<queue> 7 #include<stack> 8 #include<bitset> 9 #include<algorithm> 10 #include<string> 1
noip 2012 开车旅行
/*考场上写的暴力 40分钟70分*/ #include<iostream> #include<cstdio> #include<cstring> #define base 1000000000 #define maxn 100010 #define ll long long using namespace std; ll n,m,h[maxn],X,A[maxn],B[maxn],st,ans; bool falg; double mii=base; ll init(