1. 程式人生 > >NOIP 2012

NOIP 2012

binary rom sizeof 開車 const light date .com 子節點

  • 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