计蒜客 2017 NOIP 提高组模拟赛(二)Day2

来源:互联网 发布:网络信息工程师 编辑:程序博客网 时间:2024/05/16 07:08

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 longusing 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 998244353using 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 100005using 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 100005using 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


阅读全文
0 0
原创粉丝点击