計蒜客 2017 NOIP 提高組模擬賽(二)Day2
阿新 • • 發佈:2019-02-14
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