1. 程式人生 > >計蒜客 2017 NOIP 提高組模擬賽(二)Day2

計蒜客 2017 NOIP 提高組模擬賽(二)Day2

T1:劫富濟貧

這題一開始hash做的,超時

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<map>
#include<set>
#define MAXN 3000005
#define MOD1 12000017
#define MOD2 15000127
#define ll long long
#define pii pair<ll,ll>
using namespace std;
map<pii,ll> p;
ll read1(){
    ll ret=0;
    char c=getchar();
    do{
        ret=ret*10+c-'0';
        c=getchar();
    }while('0'<=c&&c<='9');
    return ret;
}
pii read2(){
    ll ret1=0,ret2=0;
    char c=getchar();
    do{
        ret1=ret1*29+(c-64);
        ret2=ret2*37+(c-64);
        c=getchar();
    }while('a'<=c&&c<='z');
    return make_pair(ret1,ret2);
} 
int main()
{
//    freopen("liverpool8.in","r",stdin);
//    freopen("T1.out","w",stdout);
    int n=read1();
    for(int i=1;i<=n;i++){
        pii t1=read2();
        int t2=read1();
        p.insert(map<pii,ll>::value_type(t1,t2));
    }
    int m=read1();
    for(int i=1;i<=m;i++){
        int x=read1();
        ll ans=0;
        int ok=1;
        for(int j=1;j<=x;j++){
            pii t=read2();
            if(p.count(t)){
                ans+=p[t];
            }    
            else{
                ok=0;
            }    
        }
        if(ok)
            printf("%lld\n",ans);
        else
            printf("-1\n");
    }
    return 0;
}
正解是字典樹:
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>

using namespace std;
struct Trie{
	Trie *Next[26];
	int Val;
	Trie(){
		memset(Next,0,sizeof(Next));
		Val=-1;
	}
};
int read(){
	int ret=0;
	char c=getchar();
	do{
		ret=ret*10+c-'0';
		c=getchar();
	}while('0'<=c&&c<='9');
	return ret;
}
Trie *root;
int main()
{
//	freopen("T2.in","r",stdin);
	root=new Trie;
	int n=read();
	for(int i=1;i<=n;i++){
		Trie *p=root,*q;
		while(1){
			char c=getchar();
			if('a'<=c&&c<='z'){
				c-=97;
				if(p->Next[c]){
					p=p->Next[c];
				}
				else{
					q=new Trie;
					p->Next[c]=q;
					p=p->Next[c];
				}
			}
			else{
				q->Val=read();
				break;
			}
		}
	}
	int T=read();
	for(int i=1;i<=T;i++){
		int x=read();
		int ok=1;
		long long ans=0;
		for(int j=1;j<=x;j++){
			Trie *p=root;
			while(1){
				char c=getchar();
				if('a'<=c&&c<='z'){
					c-=97;
					if(p->Next[c]){
						p=p->Next[c];
					}
					else{
						ok=0;
					}
				}
				else{
					if(p->Val!=-1){
						ans+=p->Val;
					}		
					else{
						ok=0;
					}
					break;
				}
			}	
		}
		if(!ok){
			printf("-1\n");
		}
		else{
			printf("%lld\n",ans);
		}
	}
	return 0;
}

T2:紫色百合

當時時間不夠了,草草寫了一個暴力:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<vector>
#define MOD 998244353
#define ll long long
using namespace std;
int n;
ll p;
vector<ll> s;
ll find(int k,ll sum){
    ll ret=sum;
    for(int i=k;i<s.size();i++){
        ret+=find(i+1,sum*s[i]);
    }
    return ret;
}
int check(){
    ll cnt=1;
    for(int i=0;i<s.size();i++){
        cnt+=find(i+1,s[i]);
    }
    if(cnt==p){
        return 1;
    }
    return 0;
}
int main()
{
//    freopen("T2.in","r",stdin);
    int ans=0;
    scanf("%d%lld",&n,&p);
    p=(1<<p);
    for(int i=0;i<(1<<n);i++){
        int t=0;
        s.clear();
        for(int k=i,p=1;k;k>>=1,p++){
            if(k&1){
                s.push_back((1<<p)-1);
            }
        }
        if(check()){
            ans++;
            if(MOD==ans){
                ans=0;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}
後來發現經過數學變換可以轉化為:

在1~n中選若干個數,使得它們的和為P,共有多少方案

用簡單遞推,超時:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define MAXN 100005
#define MOD 998244353
using namespace std;
int f[MAXN];
int n,p;

int main()
{
	scanf("%d%d",&n,&p);
	f[0]=1;
	for(int i=1;i<=n;i++){
		for(int j=p;j>=i;j--){
			f[j]=(f[j]+f[j-i])%MOD;
		}
	}
	printf("%d\n",f[p]);
	return 0;
}
正解是考慮最多隻能選O(sqrt(P))個物體,原因是(1+n)*n/2應該小於等於P

充分利用這樣的性質做個遞推式:f[i][j]=f[i-1][j-i]+f[i][j-i]-f[i-1][j-(N+1)] AC

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MOD 998244353
#define MAXN 100005
using namespace std;
int n,p;
int f[450][MAXN];
int main()
{
//	freopen("data.in","r",stdin);
	scanf("%d%d",&n,&p);
	for(int i=1;i<=n;i++){
		f[1][i]=1;
	}
	int Q=min(n,(int)(sqrt((double)(p<<1))));
	for(int i=2;i<=Q;i++){
		for(int j=i;j<=p;j++){
			f[i][j]=(f[i-1][j-i]+f[i][j-i])%MOD;
			if(j>n){
				f[i][j]=(f[i][j]-f[i-1][j-(n+1)]+MOD)%MOD;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=Q;i++){
		ans=(ans+f[i][p])%MOD;
	}
	printf("%d\n",ans);
	return 0;
}
這種數學題目感覺挺難的,各種細節都要考慮到

T3:銀河戰艦

一開始做了線段樹,不考慮旋轉的情況了,

但仍然很難寫,要定義三個懶標記優先順序

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define MAXN 100005
#define pii pair<double,double>
using namespace std;
pii dat[MAXN*4];
pii add[MAXN*4];
int dui[2][MAXN*4];
bool change[MAXN*4];
int n;
pii a[MAXN];
bool NZ(pii t){
	return (t.first!=0||t.second!=0);
}
void build(int k,int L,int R){
	if(L>=R){
		return;
	}
	dui[1][k]=dui[0][k]=1;
	if(L+1==R){
		dat[k]=a[L];
		return;
	}
	int mid=((L+R)>>1);
	build(k<<1,L,mid);
	build((k<<1)|1,mid,R);
}
void pushdown(int k){
	int lc=(k<<1),rc=((k<<1)|1);
	if(NZ(add[k])){
		dat[k].first+=add[k].first;
		dat[k].second+=add[k].second;
		if(!change[lc]){
			add[lc].first+=dui[1][lc]*add[k].first;
			add[lc].second+=dui[0][lc]*add[k].second;
		}
		else{
			add[lc].first+=dui[1][lc]*add[k].second;
			add[lc].second+=dui[0][lc]*add[k].first;			
		}
		if(!change[rc]){
			add[rc].first+=dui[1][rc]*add[k].first;
			add[rc].second+=dui[0][rc]*add[k].second;
		}
		else{
			add[rc].first+=dui[1][rc]*add[k].second;
			add[rc].second+=dui[0][rc]*add[k].first;			
		}
		add[k].first=add[k].second=0;		
	}
	if(-1==dui[1][k]){
		dat[k].first*=dui[1][k];
		if(!change[lc]){
			dui[1][lc]*=dui[1][k];
		}
		else{
			dui[0][lc]*=dui[1][k];			
		}
		if(!change[rc]){
			dui[1][rc]*=dui[1][k];
		}
		else{
			dui[0][rc]*=dui[1][k];
		}
		dui[1][k]=1;	
	}
	if(-1==dui[0][k]){
		dat[k].second*=dui[0][k];
		if(!change[lc]){
			dui[0][lc]*=dui[0][k];
		}
		else{
			dui[1][lc]*=dui[0][k];			
		}
		if(!change[rc]){
			dui[0][rc]*=dui[0][k];
		}
		else{
			dui[1][rc]*=dui[0][k];
		}
		dui[0][k]=1;		
	}
	if(change[k]){
		swap(dat[k].first,dat[k].second);
		change[lc]=(!change[lc]);
		change[rc]=(!change[rc]);
		change[k]=0;
	}
}
void Add(int a,int b,int k,int L,int R,pii t){
	if(b<=L||R<=a){
		return;
	}
	else if(a<=L&&R<=b){
		if(NZ(add[k])||dui[1][k]||dui[0][k]||change[k]){
			pushdown(k);
		}
		add[k].first+=t.first;
		add[k].second+=t.second;
	}
	else{
		if(NZ(add[k])||dui[1][k]||dui[0][k]||change[k]){
			pushdown(k);
		}
		Add(a,b,k<<1,L,(L+R)>>1,t);
		Add(a,b,(k<<1)|1,(L+R)>>1,R,t);
	}
}
void Dui(int a,int b,int k,int L,int R,int t){
	if(b<=L||R<=a){
		return;
	}
	else if(a<=L&&R<=b){
		if(NZ(add[k])||dui[1][k]||dui[0][k]||change[k]){
			pushdown(k);
		}
		dui[t][k]*=-1;
	}
	else{
		if(NZ(add[k])||dui[1][k]||dui[0][k]||change[k]){
			pushdown(k);
		}
		Dui(a,b,k<<1,L,(L+R)>>1,t);
		Dui(a,b,(k<<1)|1,(L+R)>>1,R,t);
	}
}
void Change(int a,int b,int k,int L,int R){
	if(b<=L||R<=a){
		return;
	}
	else if(a<=L&&R<=b){
		change[k]=!(change[k]);
	}
	else{
		if(NZ(add[k])||dui[1][k]||dui[0][k]||change[k]){
			pushdown(k);
		}
		Change(a,b,k<<1,L,(L+R)>>1);
		Change(a,b,(k<<1)|1,(L+R)>>1,R);
	}
}
void Push(int k,int L,int R){
	if(L>=R){
		return;
	}
	if(NZ(add[k])||dui[1][k]||dui[0][k]||change[k]){
		pushdown(k);
	}
	if(L+1==R){
		a[L]=dat[k];
		return;
	}
	int mid=((L+R)>>1);
	Push(k<<1,L,mid);
	Push((k<<1)|1,mid,R);	
}
int main()
{
//	freopen("T3.in","r",stdin);
//	freopen("my.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf%lf",&a[i].first,&a[i].second);
	}
	build(1,1,n+1);
	int m;
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		char c;
		int L,R;
		scanf(" %c",&c);
		scanf("%d%d",&L,&R);
		if('M'==c){
			pii t;
			scanf("%lf%lf",&t.first,&t.second);
			Add(L,R+1,1,1,n+1,t);
		}
		else if('X'==c||'Y'==c){
			int t=((c=='Y')?1:0);
			Dui(L,R+1,1,1,n+1,t);
		}
		else if('O'==c){
			Change(L,R+1,1,1,n+1);
		}
//		debug(i);
	}
	Push(1,1,n+1);
	for(int i=1;i<=n;i++){
		if(0==a[i].first){
			printf("0.00 ");
		}
		else{
			printf("%.2f ",a[i].first);
		}
		if(0==a[i].second){
			printf("0.00\n");
		}
		else{
			printf("%.2f\n",a[i].second);
		}
	}
	return 0;
}
後來發現所有操作都可視為矩陣線性變換,

即(x y 1)乘不同的矩陣即可進行不同的操作,

然後矩陣線段樹,這樣只會有一種乘的操作了

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100005
using namespace std;
struct Mat{
	int x,y;
	double s[3][3];
	Mat(){
		x=y=3;
		memset(s,0,sizeof(s));
		s[0][0]=s[1][1]=s[2][2]=1;
	}
	Mat operator * (const Mat &A){
		Mat ret;
		ret.x=x; ret.y=A.y;
		memset(ret.s,0,sizeof(ret.s));
		for(int i=0;i<x;i++){
			for(int j=0;j<A.y;j++){
				for(int k=0;k<y;k++){
					ret.s[i][j]+=s[i][k]*A.s[k][j];
				}
			}
		}
		return ret;
	}
};
int n;
double pi=acos(-1);
Mat dat[MAXN*4];
Mat a[MAXN];
void pushdown(int k){
	dat[k<<1]=dat[k<<1]*dat[k];
	dat[(k<<1)|1]=dat[(k<<1)|1]*dat[k];
	for(int i=0;i<3;i++){
		for(int j=0;j<3;j++){
			if(i==j){
				dat[k].s[i][j]=1;
			}
			else{
				dat[k].s[i][j]=0;
			}
		}
	}
}
void Add(int a,int b,int k,int L,int R,Mat t){
	if(b<=L||R<=a){
		return;
	}
	else if(a<=L&&R<=b){
		dat[k]=dat[k]*t;
	}
	else{
		pushdown(k);
		Add(a,b,k<<1,L,(L+R)>>1,t);
		Add(a,b,(k<<1)|1,(L+R)>>1,R,t);
	}
}
void Push(int k,int L,int R){
	if(L+1==R){
		a[L]=a[L]*dat[k];
		for(int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				if(i==j){
					dat[k].s[i][j]=1;
				}
				else{
					dat[k].s[i][j]=0;
				}
			}
		}
		return;
	}
	pushdown(k);
	Push(k<<1,L,(L+R)>>1);
	Push((k<<1)|1,(L+R)>>1,R);
}
void debug(){
	Push(1,1,n+1);
	for(int i=1;i<=n;i++){
		printf("%.2f %.2f\n",a[i].s[0][0],a[i].s[0][1]);
	}
	printf("\n");
}
int main()
{
//	freopen("T3.in","r",stdin);
//	freopen("my.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		double x,y;
		scanf("%lf%lf",&x,&y);
		a[i].x=1; a[i].y=3;
		memset(a[i].s,0,sizeof(a[i].s));
		a[i].s[0][0]=x,a[i].s[0][1]=y,a[i].s[0][2]=1;
	}
//	debug();
	int T;
	scanf("%d",&T);
	for(int i=1;i<=T;i++){
		char c;
		int L,R;
		scanf(" %c",&c);
		scanf("%d%d",&L,&R);
		if('M'==c){
			double x,y;
			scanf("%lf%lf",&x,&y);
			Mat t;
			t.s[2][0]=x; t.s[2][1]=y;
			Add(L,R+1,1,1,n+1,t);
		}
		else if('X'==c){
			Mat t;	
			t.s[1][1]=-1;
			Add(L,R+1,1,1,n+1,t);
		}
		else if('Y'==c){
			Mat t;
			t.s[0][0]=-1;
			Add(L,R+1,1,1,n+1,t);
		}
		else if('O'==c){
			Mat t;
			t.s[0][0]=t.s[1][1]=0;
			t.s[0][1]=t.s[1][0]=1;
			Add(L,R+1,1,1,n+1,t);
		}
		else{
			double aa;
			scanf("%lf",&aa);
			aa=aa*pi/180;
			Mat t;
			t.s[0][0]=cos(aa); t.s[0][1]=sin(aa);
			t.s[1][0]=-sin(aa); t.s[1][1]=cos(aa);
			Add(L,R+1,1,1,n+1,t);			
		}
//		debug();
	}
	Push(1,1,n+1);
	for(int i=1;i<=n;i++){
		printf("%.2f %.2f\n",a[i].s[0][0],a[i].s[0][1]);
	}
	return 0;
}
總結:

這次模擬題說實話真的挺難的,我沒得幾分,細節方面經常炸

多注意細節還是很有必要啊ToT