1024 模拟赛
写在前面
这次模拟赛,难度。。。还可以吧。因为是欢乐赛,所以打的很开心
T1序列(sequence)
Idea
本来想开个桶的,然后看到了\(a_i \le 10^9\)。于是我就开了个map
这算是道模拟题吧。具体看代码,很好懂的。
Code
namespace Sol{
map<int,int> m;//1e9,不现实
int n,tot,t=1,ans=inf,a[1005];//t要从1开始
inline int Main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
if(m[a[i]]==1) tot++;//记录一个数的个数
m[a[i]]++;
}
for(int i=1;i<=n;i++){
m[a[i]]--;
if(m[a[i]]==1) tot--;
while(!m[a[t]]&&t<i) m[a[t++]]++;
if(tot==0) ans=min(ans,i-t+1);
}
printf("%d",ans==1?0:ans);//ans=1,表示没有删减
return 0;
}
}
T2数字(number)
Idea
一道数学题。找规律即可。
我们一定知道,所有数的倍数的个位数是会循环的~~
例如:
2:2 4:4 6:6 8:8 10:0 12:2 ....
3:3 6:6 9:9 12:2 15:5 18:8 21:1 24:4 27:7 30:0 33:3......
//以此类推
题目让求的是 求 \(1 \sim n\) 的所有整数中,能被 \(m\) 整除的整数的个位数字之和 。
我们发现了个位数字是循环的,于是我们就靠这个来优化。
发现:当\(x\%10=0\)时 ,\(x\)为\(m\)的倍数,一轮循环结束。计算这轮循环的答案\(Ans\)
然后我们判断\(1 \sim n\)中有多少个\(x\)。假设有\(a\)个,则这\(a\)组的值为\(a \times Ans\)。
最后计算余下的数中个位之和即可。讲的不是很清,大家自己手玩几组就知道了。
可以借助代码理解哦~~
Code
namespace Sol{
int ans;
inline int Main(){
int T=read();
while(T--){
int n=read(),m=read(); ans=0;
for(int i=m;i<=n;i+=m){
if(i%10==0){
ans*=n/i;//n/i是看有多少组,然后就可以跳过这n/i组
i*=n/i;//跳到了i*=n/i这个数
}
ans+=i%10;//计算个位和
}
printf("%lld\n",ans);
}
return 0;
}
}
T3背包(knapsack)
Idea
knapsack?好奇怪的名字
这道题,是个\(DP\)?还是我能写的那种
这道题按\(out\)为第一关键字,\(in\)为第二关键字排序。
即先把所有物品按照拿走的时间从小到大排序,拿走的时间相同就按照放上去的时 间从大到小。那么一件物品上方的物品就一定会在它的前面。
这里设 \(f[i][j]\)表示\(i\) 以及\(i\) 上面的物品在所有时刻中最大重量为 \(j\) 时的最 大收益。
转移的时候,枚举所有 \(i\) 上面的物品,维护一个 \(g[i]\)表示时刻 \(i\) 之前 物品的最大收益是多少。然后转移。
目标\(f[n][S]\)
真的很简单
Code
namespace Sol{
struct node{
int in,out,w,s,v;
}a[505];
int f[505][maxn],g[maxn];
int n,S;
inline bool cmp(node a,node b){
if(a.out==b.out) return a.in>b.in;
return a.out<b.out;
}
inline int Main(){
n=read(); S=read();
for(int i=1;i<=n;i++)
a[i]=(node){read(),read(),read(),read(),read()};
a[++n]=(node){0,n*2,0,S,0};
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
for(int w=a[i].w;w<=S;w++){
int x=a[i].in; g[x]=0;
int y=min(w-a[i].w,a[i].s);
for(int j=1;j<i;j++)
if(a[j].in>=a[i].in){
while(x<a[j].out)x++,g[x]=g[x-1];
g[x]=max(g[x],g[a[j].in]+f[j][y]);
}
f[i][w]=g[x]+a[i].v;
}
printf("%d",f[n][S]);
return 0;
}
}
T4 玩具(toy)
Idea
一道 状压。又是状压,草
这里上题解。因为我太菜了,不会解释
考虑这个题其实就是给我们若⼲个⼆进制数,问最终有多少种选数的⽅案使得or(即\(\oplus\)异或)起来全部是1。
令\(cnt(S)\)表示等于\(S\)的⼆进制数个数,令\(f(S)=\displaystyle \sum_{S' \subseteq S}cnt(S')\) ,那么⼀个显然的容斥有答案为\(\displaystyle \sum_{S} 2^{f(S)} (-1)^{n-\mid S \mid}\)
所以唯⼀的难点在于怎么求\(f(S)\),如果直接暴⼒求复杂度是\(3^M\),可以获得\(70\ pts\)。 这其实就是⼀个⾼维前缀和问题。令\(f_{i,j}\)为在不考虑前\(i\)位的情况下,\(j\)的前缀和。 ⾸先,显然有\(f_{i,j}=cnt_{i}\) ,其次\(f_{i,j}=f_{i+1,j}+f_{j \mid (1<<i)}\),其中\(j\)的第\(i\)位是0. 直接⼀个递推就做完了
\(\mathcal {P.S:}\)注意爆负。
Code
namespace Sol{
int f[maxn],g[maxn],s[maxn],a[maxn];
inline int Main(){
int n=read(),m=read();
a[0]=1;
for(int i=1;i<=n;i++){
int x=read();
for(int j=1;j<=x;j++) s[i]+=(1<<(read()-1));
g[s[i]]++; a[i]=(a[i-1]<<1)%mod;
}
for(int j=0;j<m;j++)
for(int i=0;i<(1<<m);i++)
if(i&(1<<j)) g[i]+=g[i-(1<<j)];
for(int i=0;i<(1<<m);i++)
f[i]=a[g[i]];
for(int j=0;j<m;j++)
for(int i=0;i<(1<<m);i++)
if(i&(1<<j)) f[i]=((f[i]-f[i-(1<<j)])%mod+mod)%mod;
printf("%d",f[(1<<m)-1]);
return 0;
}
}