noip 2018 模拟赛13

T 1 T_1 T1——nim(3833)

Description:

n n n堆石子,第 i i i堆的石子个数为 i i i,两人可以每次可以在其中一堆中拿任意数量的石子,无法再继续拿为败。求必胜的方案数。
n ≤ 1 0 1000 n\le 10^{1000} n101000

Solution:

  • 由于出题人的疯狂,强行练一练高精度,
  • 此题可以说是博弈题,也可以说是找规律题(你甚至可以在大样例中找规律
  • 具体的规律是:
  • n n n为偶数,答案为 n − 2 m x + 1 n-2^{mx}+1 n2mx+1
  • n n n为奇数且 ( n + 1 ) 2 \frac{(n+1)}{2} 2(n+1)也为奇数时,答案为 ( n + 1 ) 2 \frac{(n+1)}{2} 2(n+1)
  • 待证明

Code:

#include
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long

#define N 1002
#define L 255
#define P 10000

struct Big{
	int num[L],len;
	Big(){
		memset(num,0,sizeof(num));
		len=1;
	}
	void Rd(){
		char A[N];
		scanf("%s",A);
		int SL=strlen(A);
		len=0;
		for(int i=SL-1,res;res=0,i>=0;num[len++]=res,i-=4){
			if(i>=3)for(int j=i-3;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
			else for(int j=0;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
		}
	}
	void Pr(){
		if(!num[len-1]){putchar('0');return;}
		printf("%d",num[len-1]);
		DREP(i,len-2,0)printf("%04d",num[i]);		
	}
	Big operator +(const Big &a)const{
		Big b;
		b.len=max(len,a.len);
		REP(i,0,b.len-1){
			int &B=b.num[i];
			B+=num[i]+a.num[i];
			if(B>=P)B-=P,b.num[i+1]++;
		} 
		if(b.num[b.len])b.len++;
		return b;
	}
	Big operator+(int a){
		Big b;
		b.len=len;
		REP(i,0,len-1){
			int &B=b.num[i];
			B+=num[i]+a%P;
			if(B>=P)b.num[i+1]++,B-=P;
			a/=P;
		}
		if(b.num[b.len])b.len++;
		return b;
	}
	Big operator -(const Big &a)const{
		Big b;
		REP(i,0,len-1)b.num[i]=num[i];
		b.len=len;
		DREP(i,a.len-1,0){
			b.num[i]-=a.num[i];
			int now=i;
			while(b.num[now]<0)b.num[now]+=P,now++,b.num[now]--;
		}
		while(b.len>1 && !b.num[b.len-1])b.len--;
		return b;
	} 
	Big operator *(const int &a)const{
		Big b;
		b.len=len;
		REP(i,0,len-1){
			int &B=b.num[i];
			B+=num[i]*a;
			if(B>=P)b.num[i+1]+=B/P,B%=P;
		}
		if(b.num[b.len])b.len++;
		return b;
	}
	Big operator /(const int &a)const{
		Big b;
		REP(i,0,len-1)b.num[i]=num[i];
		DREP(i,len-1,1)b.num[i-1]+=b.num[i]%a*P,b.num[i]/=a;
		b.num[0]/=a;
		b.len=len;
		while(b.len>1 && !b.num[b.len-1])b.len--;
		return b;
	}
	bool operator<=(const Big &a)const{
		if(len!=a.len)return len>cas;
	Big n;
	while(cas--){
		n.Rd();
		if(n.num[0]&1){
			n=n+1;
			n=n/2;
			if(n.num[0]&1)n.Pr();
			else putchar('0');
		}
		else {
			Big mx;
			mx=mx+1;
			while(mx<=n)mx=mx*2;
			mx=mx/2;
			n=n-mx+1;
			n.Pr();
		}
		puts("");
	}
	return 0;
}

T 2 T_2 T2——segment(3056)

Description:

一个长度为 n n n的序列 A A A,现在想求出有多少对区间 [ a , b ] [a,b] [a,b]与区间 [ c , d ] [c,d] [c,d],满足不存在一个数在 { A a , A a + 1 , . . . , A b − 1 , A b } \{A_a,A_{a+1},...,A_{b-1},A_b\} {Aa,Aa+1,...,Ab1,Ab} { A c , A c + 1 , . . . , A d − 1 , A d } \{A_c,A_{c+1},...,A_{d-1},A_d\} {Ac,Ac+1,...,Ad1,Ad}中同时出现。
n ≤ 3000 , 1 ≤ A i ≤ 1 0 9 n\le3000,1\le A_i\le 10^9 n3000,1Ai109

Solution:

Code:

#include
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long

const int N=3002;

int n;
int A[N];
struct p30{
	mapmp;
	void solve(){
		int ans=0;
		REP(a,1,n) REP(b,a,n) {
			mp.clear();
			REP(i,a,b) mp[A[i]]++;
			REP(c,b+1,n) REP(d,c,n){
				bool flag=1;
				REP(j,c,d) if(mp[A[j]]){flag=0;break;}
				ans+=flag;
			}
		}
		printf("%d\n",ans);
	}
}p1;

struct p100{
	int tmp[N];
	vectorpos[N];
	int cnt[N];
	int pre[N],nxt[N];
	int val[N];
	void solve(){
		memcpy(tmp,A,sizeof(tmp));
		sort(tmp+1,tmp+1+n);
		REP(i,1,n) {
			A[i]=upper_bound(tmp+1,tmp+1+n,A[i])-tmp-1;
			pos[A[i]].push_back(i);
		}
	
		REP(i,1,n) val[i]=i*(i+1)/2;
	
		ll ans=0;
		REP(i,2,n){
			REP(j,1,n) cnt[j]=0;
			REP(j,i,n) cnt[A[j]]++;
			int last=0;
			REP(j,1,i) if(cnt[A[j]]){
				nxt[last]=j;
				pre[j]=last;
				last=j;
			}
			ll sum=0;
			for(int j=0;j=i)break;
						int L=pre[x],R=nxt[x];
						sum-=val[R-x-1];
						sum-=val[x-L-1];
						pre[R]=L,nxt[L]=R;
						sum+=val[R-L-1];
					}
				}
				ans+=sum;
			}
		}
		
		printf("%lld\n",ans);
	}
}p2;

int main(){
//	freopen("segment.in","r",stdin);
//	freopen("segment.out","w",stdout);
	scanf("%d",&n);
	REP(i,1,n) scanf("%d",&A[i]);
	
	if(n<=20)p1.solve();
	else p2.solve();
	
	return 0;
}

T 3 T_3 T3——network(3834)

Description:
NOI 2006 网络收费

Solution:

Code:

#include
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);iinline bool chkmin(T &x,T y){return x>y?x=y,1:0;} 
const int N=(1<<11)+2,INF=0x3f3f3f3f;

int n,m;
int T[N],C[N],F[N][N];
int cost[N][N];
int dp[N][N];
int Pow[12];

int Lca(int x,int y){
	while(x!=y)x>>=1,y>>=1;
	return x;
}

void solve(int p,int dep){
	if(!dep){
		dp[p][T[p]]=0,dp[p][!T[p]]=C[p];
		for(int i=p>>1;i;i>>=1) dp[p][T[i]]+=cost[i][p];
		return;
	}
	
	memset(dp[p],63,sizeof(dp[p]));
	
	T[p]=0;
	solve(p<<1,dep-1),solve(p<<1|1,dep-1);
	REP(i,1,Pow[dep-1]) REP(j,Pow[dep-1]-i+1,Pow[dep]-i) chkmin(dp[p][i+j],dp[p<<1][i]+dp[p<<1|1][j]);
	
	T[p]=1;
	solve(p<<1,dep-1),solve(p<<1|1,dep-1);
	REP(i,0,Pow[dep-1]) REP(j,0,Pow[dep-1]-i) chkmin(dp[p][i+j],dp[p<<1][i]+dp[p<<1|1][j]);
	 
}

int main(){
//	freopen("network.in","r",stdin);
//	freopen("network.out","w",stdout);
	scanf("%d",&n);
	Pow[0]=1;
	REP(i,1,10) Pow[i]=Pow[i-1]+Pow[i-1];
	
	REP(i,1,Pow[n]) scanf("%d",&T[i+Pow[n]-1]);
	REP(i,1,Pow[n]) scanf("%d",&C[i+Pow[n]-1]);
	SREP(i,1,Pow[n]) REP(j,i+1,Pow[n]){
		int x=i+Pow[n]-1,y=j+Pow[n]-1;
		scanf("%d",&F[x][y]);
		int lca=Lca(x,y);
		cost[lca][x]+=F[x][y],cost[lca][y]+=F[x][y];
	}
	
	solve(1,n);
	
	int ans=INF;
	REP(i,0,Pow[n]) chkmin(ans,dp[1][i]);
	printf("%d\n",ans);
	
	return 0;
}

你可能感兴趣的:(离线赛-总结)