计蒜客 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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值