模拟69 考试总结
久远 沉寂
考试经过
开场T1博弈论,发现还是蓝书颓少了,根本不会……T2发现是模拟,直接开打,一波样例走人
T3题目唬人,结果根本和最优策略没一点关系,就一数学式子,然后看见gcd化了一波,认为是莫反的神仙题,于是签到失败
T4数位dp看来很可做,直接开打,写完发现貌似不太对,看大样例得知要写高静,略微权衡一下,一个小时可以莽一下,果断打压位高静,两分钟结束的时候过了样例,直接交上不管了
交完发现数组算小了,于是改大了数组过了大样例,结果MLE了,一分没有
accoder得分:0+40+30+70=140,oj得分:0+30+40+0=70
T1.石子游戏
入门了一下博弈论,发现是一个NIM博弈的变形,考虑当前的\(a_i\)大于\(x\)的时候,一定可以通过二人取相同的来将其变为\(x\)一下,于是直接模\(x+1\),50分到手,接下来思考如何优化
对于每个\(x\),\(c_x\)表示\(a_i=x\)的数量
结果是异或和,按照每一位考虑贡献
从读入开始下标就已经没有意义了,以下均为值域上操作
暴力\(n^2\),由于要枚举\(x\),所以应该考虑如何快速计算
难点在于每次的\(x\)都要取模,发现在一段连续值域上的余数不变
枚举\(k<=n/y\),对于\([ky,(k+1)y)\) 的余数即为\(x-ky\)
枚举每个二进制位\(j\),则这段区间这一位上的答案就是区间中的\(t\),满足\(t-ky\)包含\(j\)这一位的(所有\(c_t\)的和)\(&1\),因为异或看奇偶个数
倍增预处理
\(f_{i,j}\)表示对于所有\(x>=i\),满足\(x-i\)中\(j\)这一位是1的\(c_x\)之和
那么我们已经有了dp数组,答案应该可以快速得到了,然而题解说了啥?
一些加减。
草(一种植物)
因为我们的\(f\)数组是后缀和的形式,朴素的想法是直接相减,但这是不对的,仔细观察dp定义,要求的是\(t-ky\),然而dp数组里是\(x-i\),如果相减的话\(f_{ky,j}\),\(f_{(k+1)y-1),j}\)减得东西不一样,所以不能直接做差
怎么办?我们再审视一下dp数组,为什么原来的dp数组没有遇到过这个问题呢?
他的计算形式先加了\(f_{i+2^{j+1},j}\),这个没事的原因是你要算第\(j\)位的贡献,它先累加了\(j+1\)位以上的大数,高位不影响低位,所以减去之后依然能完全算入贡献,后面的求和就是钦定第\(j\)位为1,然后考虑比他低的位有多少个
那么有了这个式子我们得到一些启发,可以用dp数组的思想来拼接答案,我们还发现,对刚才dp扩展一下也是成立的
只要乘的大于\(2^{j+1}\),就都成立,上面乘一个系数也一样
那么我们的思路就是找到最靠近\((k+1)y-1\)的2的整次幂,把它减掉,再用前缀和加回来,会发现和dp差不多
核心代码 :
int l=k*y,r=min((k+1)*y-1,n),len=r-l+1;
int p=len>>(j+1),s=f[l][j]-f[l+p*bit[j+1]][j];
if(l+p*bit[j+1]+bit[j]-1<=r)s+=sum[r]-sum[l+p*bit[j+1]+bit[j]-1];
判一下因为右边不能减超,也是因为后面的位数只有到了\(j\)才有贡献,否则不用算,没有贡献
#include <bits/stdc++.h>
using namespace std;
const int N=500050;
int a[N],c[N],lg[N];
int f[N][22],bit[22],sum[N];
signed main()
{
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
int n;cin>>n;
for(int i=1;i<=n+1;i++)lg[i]=log2(i);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)c[a[i]]++;
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+c[i];
bit[0]=1;for(int i=1;i<=20;i++)bit[i]=bit[i-1]*2;
for(int i=n;i>=0;i--)
for(int j=0;j<=lg[n-i+1];j++)
f[i][j]=f[min(i+bit[j+1],n+1)][j]+sum[min(i+bit[j+1]-1,n)]-sum[i+bit[j]-1];
for(int x=1;x<=n;x++)
{
int y=x+1,ans=0;
for(int j=0;j<=lg[x];j++)
{
for(int k=0;k<=n/y;k++)
{
int l=k*y,r=min((k+1)*y-1,n),len=r-l+1;
int p=len>>(j+1),s=f[l][j]-f[l+p*bit[j+1]][j];
if(l+p*bit[j+1]+bit[j]-1<=r)s+=sum[r]-sum[l+p*bit[j+1]+bit[j]-1];
if(s&1)ans^=1<<j;
}
if(ans)break;
}
if(ans)printf("Alice ");
else printf("Bob ");
}
return 0;
}
边界极其恶心,你会发现不注意的话预处理出来一堆负数
记得经常取min保证不会越界,将\(j,k\)换一下按每一位考虑,如果这一位是1那么以后不会对他有影响,结果已经确定,剪枝
T2.大鱼吃小鱼
数据结构,还没做,咕了,%%%Yubai考场84高分
T3.黑客
被斥为普及的签到题,签到失败x1
发现最终只有999,\(n^2\)枚举,做完了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
inline int gcd(int x,int y)
{
if(!y)return x;
return gcd(y,x%y);
}
inline int gan(int n,int m)
{
int ans=0;
for(int i=1;i<=min((int)999,n);i++)
for(int j=1;j<=min(999-i,m);j++)
{
if(gcd(i,j)!=1)continue;
ans=(ans+min(n/i,m/j)*(i+j)%mod)%mod;
}
return ans;
}
signed main()
{
freopen("hacker.in","r",stdin);
freopen("hacker.out","w",stdout);
int a,b,c,d;cin>>a>>b>>c>>d;
cout<<(gan(b,d)-gan(a-1,d)-gan(b,c-1)+gan(a-1,c-1)+2*mod)%mod;
return 0;
}
程序中必须包含函数才算A掉
T4.黑客(续)
感觉真没啥难的,除了高精没有难点,直接放码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int ban[15],n,m,k;
const int mod=1e9;
struct nbint{
int sum[110],len=1;
inline void mem(int x){memset(sum,0,sizeof(sum));sum[1]=x;len=1;}
inline void print()
{
for(int i=len;i>=1;i--)
{
if(i==len)printf("%lld",sum[i]);
else printf("%09lld",sum[i]);
}
puts("");
}
inline bool pd(){if(len==1&&sum[1]==-1)return 1;else return 0;}
};
nbint ans,ga;
inline nbint add(nbint x,nbint y)
{
ans.mem(0);
if(x.len<y.len)swap(x,y);
int ga=0;ans.len=max(x.len,y.len);
for(int i=1;i<=ans.len;i++)
{
ans.sum[i]=(x.sum[i]+y.sum[i]+ga)%mod;
ga=(x.sum[i]+y.sum[i])/mod;
}
if(ga)ans.sum[++ans.len]=ga;
return ans;
}
inline nbint mul(nbint x,nbint y)
{
ans.mem(0);
if(x.len<y.len)swap(x,y);
int l1=x.len,l2=y.len;
for(int i=1;i<=l2;i++)
{
int ga=0;
for(int j=1;j<=l1;j++)
{
int gaa=(ans.sum[i+j-1]+x.sum[j]*y.sum[i]+ga)/mod;
ans.sum[i+j-1]=(ans.sum[i+j-1]+x.sum[j]*y.sum[i]+ga)%mod;
ga=gaa;
}
if(ga)ans.sum[i+l1]+=ga;
}
ans.len=l1+l2+1;
while(!ans.sum[ans.len])ans.len--;
return ans;
}
pair<nbint,nbint> mem[510][(1<<9)+10];
nbint bit[520],b10,p1[510],p2[510];
pair<nbint,nbint> ppp;
pair<nbint,nbint> dfs(int x,int sta)
{
if(!mem[x][sta].first.pd())return mem[x][sta];
if(x>n)return mem[x][sta]=ppp;
p1[x].mem(0);p2[x].mem(0);
for(int i=1;i<=k;i++)
{
if((sta>>(i-1))&1)continue;
pair<nbint,nbint> p=dfs(x+1,sta|ban[i]);
ga.mem(i);
p1[x]=add(p1[x],p.first),p2[x]=add(p2[x],add(mul(mul(p.first,bit[n-x]),ga),p.second));
}
return mem[x][sta]=make_pair(p1[x],p2[x]);
}
signed main()
{
freopen("hacker2.in","r",stdin);
freopen("hacker2.out","w",stdout);
cin>>n>>m>>k;ans.mem(0);
ppp.first.mem(1),ppp.second.mem(0);
for(int i=1;i<=m;i++)
{
int x,y;cin>>x>>y;
ban[x]|=(1<<(y-1));
}
b10.mem(10);bit[0].mem(1);
for(int i=1;i<=n;i++)
bit[i]=mul(bit[i-1],b10);
for(int i=0;i<=505;i++)
for(int j=0;j<=(1<<9);j++)
mem[i][j].first.mem(-1);
pair<nbint,nbint> an=dfs(1,0);
an.first.print();
an.second.print();
return 0;
}
刺激
考试总结
的确是值得反思的
首先开题策略不是很对,直接跳过T1还是太草率了,很多人考试的时候都有50分,白白丢掉确实可惜
尽量不要使自己的思维很复杂,有时候题目不难而是被想复杂了
T4的高精现在想不如写部分分,都是70,还节约时间,时间是很宝贵的
发现自己的策略一直是前松后紧,最后容易慌,要在前面把暴力搞定

浙公网安备 33010602011771号