20260425 ptyb 总结
ptyb 好难啊 /ll
总分 \(100+100+80+50+65=395\),我咋这么菜。
备注:总结里的代码都没有 freopen,因为是直接从 yl503 上粘的。
A - skill
老老实实翻一下每个题的部分分然后打个表就做完了。
我也不知道为什么打二维的表总是报错,一气之下直接开了 \(4\) 个一维数组解决了。
戳这里看代码
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
#define _i128 __int128
using namespace std;
int n,m;
int a1[]={0,20,20,20,20,20};
int a2[]={0,20,20,10,30,20};
int a3[]={0,25,25,30,20};
int a4[]={0,15,15,15,10,10,15,20};
int read(){
int su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
int main(){
n=read(),m=read();
if(n==1)cout<<a1[m]<<"\n";
if(n==2)cout<<a2[m]<<"\n";
if(n==3)cout<<a3[m]<<"\n";
if(n==4)cout<<a4[m]<<"\n";
return 0;
}
B - leg
怎么感觉我的方法和大家的都不一样 /yiw 或者说其实本质是一样的?我也不知道。
大概就是说 \(a+d\) 和 \(b+c\) 的效果都等价于从 \((x,y)\) 挪到 \((x+1,y+1)\),所以我们先求出 \(\min(a+d,b+c)\) 然后如上移动到 \((x,m)\) 或 \((n,x)\),再对应移步数过去就行了。
这个贪心的正确性我都不会证,赛时敢这么写是因为样例都过了 /kel
戳这里看代码
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
#define _i128 __int128
using namespace std;
LL n,m,a,b,c,d,up1,Ans;
LL read(){
LL su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
int main(){
n=read(),m=read();
a=read(),b=read(),c=read(),d=read();
up1=min(a+d,b+c);
Ans+=min(n,m)*up1;
if(n<m){//y++
m-=n;
Ans+=(m+1)/2*b+m/2*d;
}else{//x++
n-=m;
Ans+=(n+1)/2*a+n/2*c;
}
cout<<Ans<<"\n";
return 0;
}
C - calculate
现在看来觉得好好笑,当时做题的时候明明看到了 \(c\) 的位数至多 \(10\),但是我当时在打暴力,只认为这个 \(c\) 的位数限制是来避免高精乘爆炸的,却一直没想直接把 \(c\) 当做低精看待。。。如果我赛时想到了把 \(c\) 当低精,也许很快就能做出来了呢?
我也实在没想到会这么简单。我个人评这题顶多是洛谷的橙题,就很水的那种吧,我也不知道我为什么会卡了这么久。嗯,说出来我也吃惊,大概就是其实 C D E 的难度还是挺有差别的吧,全部补下来 C 显然是很简单的那种……相比之下 D 和 E 都还有些思维难度。可是,更让人吃惊的是,三道题我花的时间都差不太多,甚至说在 C 用的还久些。说实话当时我做 C 的时候还弄了半天才弄懂这个进制位。。。
其实我 C 要是早一点注意到,早一点,稍微再早一点,再早一点注意到那个 \(c\) 的位数限制,那个时候我还在想正解,肯定就会了嘛……后面就放弃了呢,看到了也不会再往正解的方向想了……多可惜……
好吧,说这么多最后我能记下来的教训只有一条:做题之前看清楚数据范围。
注意到 \(c\) 至多就 \(10^{10}\),换算成正常的数字然后直接和 \(b_i\) 去做模板高精乘即可,注意每个位置的模数都不一样,需要对应使用对应的 \(p_i\)。
戳这里看代码
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
#define _i128 __int128
using namespace std;
const int N = 1e5+5;
LL n,p[N],la,lb,ss;
LL a[N],b[N],s[N],numb;
string A,B;
int read(){
int su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
int main(){
n=read();
for(int i=0;i<n;i++)p[i]=read();
cin>>A>>B;
la=A.size(),lb=B.size();
for(int i=0;i<A.size();i++)
a[--la]=A[i]-'0';
for(int i=0;i<B.size();i++)
b[--lb]=B[i]-'0';
for(int i=B.size()-1;i>=lb;i--)
numb=numb*p[i]+b[i];
for(int i=0;i<n;i++){
s[i]+=a[i]*numb;
s[i+1]+=s[i]/p[i],s[i]%=p[i];
}ss=n-1;while(!s[ss])--ss;
for(int i=ss;i>=0;i--)cout<<s[i];cout<<"\n";
return 0;
}
D - friends
嗯……说实话这题我不太怪自己,这个子任务 \(3\) 的做法我确实是没想到……嗯,也可以说就是,唉,我做子任务 \(2\) 的时候并没有想到所谓的“从枚举答案的角度入手解决问题”,这个我实在是没想到的,可能我做子任务 \(2\) 的时候是一种反过来的方式吧?不是很想回忆了,反正根本没有想到点子上,当时走到子任务 \(2\) 就走不动了呢。。。
唉唉还有就是我这个智障脑袋看到这种题就想着去做 DP 啊什么的,其实正解和 DP 沾不上边。不要太相信自己的直觉唉,自己的直觉往往是错的唉。
还有就是,很无奈的一点,其实我想到了答案一定是总和的因子啊!!!我怎么没有去试着对总和做分解质因数啊!!!这个就很可惜吧,如果我想到了,至少……能推出来一些子任务 \(3\) 的东西,也就能想到枚举答案了……吧。
所以说……总的来说就是……如果想到了一方面的信息一定要顺着思路往下至少走几步对吧……说不定撞上正解了呢?
考虑公因数为 \(l\) 的情况至多能切成多少个段,其实等价于求前缀和中是 \(l\) 的倍数的个数。但是直接枚举还是不行的,观察到 \(l\) 一定是 \(sum = \sum a\) 的因子,那么对于一个前缀和为 \(s\) 的值,其对每个 \(l\) 产生的贡献等价于 \(\gcd(s,sum)\) 对 \(l\) 产生的贡献。
这样就只剩下 \(d(sum)\) 个不同的值了,把 \(sum\) 的所有因数都找出来,排序,lower_bound 二分找到位置并累加个数。
现在对于每个答案 \(l\) 都只需要枚举这 \(d(sum)\) 种情况了,遇到倍数就累加,最后的结果就是最大能切分的段数。
由于贡献是对于一段前缀的,可以只在末端记录,最后求一次后缀最大值即可。
戳这里看代码
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
#define _i128 __int128
using namespace std;
const int N = 1e5+5;
LL n,a[N],f[N],fcnt,c[N],ans[N];
LL read(){
LL su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
LL GCD(LL x,LL y){
if(x<y)swap(x,y);
return (y?GCD(y,x%y):x);
}
int main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=a[i-1]+read();
fcnt=0;
for(LL x=1;x*x<=a[n];x++){
if(a[n]%x!=0)continue;
f[++fcnt]=x;if(a[n]/x!=x)f[++fcnt]=a[n]/x;
}
sort(f+1,f+fcnt+1);
for(int i=1;i<=n;i++)
c[lower_bound(f+1,f+fcnt+1,GCD(a[i],a[n]))-f]++;
for(int i=1;i<=fcnt;i++){
for(int j=i+1;j<=fcnt;j++)
if(f[j]%f[i]==0)c[i]+=c[j];
ans[c[i]]=max(ans[c[i]],f[i]);
}
for(int i=n;i>=1;i--)
ans[i]=max(ans[i],ans[i+1]);
for(int i=1;i<=n;i++)
cout<<ans[i]<<" ";cout<<"\n";
return 0;
}
E - plan
我支持对赛时的自己使用“乐子”这个称呼。
不是我赛时怎么可以那么傻,我想到了状压,我想到了双指针,然后呢,正解呢?正解没想到。正解不就是这两玩意儿吗,我怎么会想不到???
好难猜呀。我难道没注意到 \(m\) 只有 \(10\) 吗?注意到了。我难道没注意到 \(2^m\) 只是 \(1000\) 级别的吗?注意到了。我难道没注意到 \(n\) 的 \(10^5\) 乘上那个 \(2^m\) 的 \(1000\) 根本不会 TLE 吗?注意到了。可是我在干什么呢?我觉得正解是线性的,我觉得是 \(O(n)\) 的,我觉得不能直接枚举 \(2^m\) 个去判,我觉得我想到的正解不是正解……
不是,这就真的搞笑来的吧,我为什么,我真的,明明正解的思路在我脑海里反复来回跳了数不清多少次,我甚至差点开始写代码,然后转念一想——这样过不去吧,直接枚举肯定会炸呀!什么意思啊?我难道没看数据范围吗?明明看了啊!怎么会呢???怎么回事呢?????
不知道,我现在也无法用任何东西掩盖这个现实了,我无法对赛时的自己做出的错误判断找任何一个借口——我也觉得无语,我也不知道为什么,明明 AC 已经出来找你了你为啥要把它赶走???
唉唉我得到的教训就是:不要把题目想的那么难,注意看数据范围,有时候想出来的所谓“暴力”可能就是正解。
真被自己气笑了吧,我打暴力,行啊,你暴力也没打对啊。。。
\(m\) 很小,只有 \(10\),启发我们用二进制状态压缩记录每个人的爱好。两个爱好分别状压记录为 \(x\) 和 \(y\) 的人不会进行交流当且仅当 \(x\) 和 \(y\) 的与值为 \(0\)。
显然加入一个人不会导致不进行交流的人数变少,干掉一个人也不会导致不进行交流的人变多,这意味着这个问题有单调性,我们可以用双指针解决问题。
双指针遍历到哪个区间,我们就实时维护该区间内所有种状压情况的人数,在加入一个人或删除一个人时,枚举每种和这个人不会进行交流的状压情况,求和即可。
时间复杂度是 \(O(n 2^m)\) 的,毫无问题啊。
戳这里看代码
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
#define _i128 __int128
using namespace std;
const int N = 1e5+5;
const int M = (1<<11)+5;
LL n,m,k,a[N],s[M],Ans;
LL read(){
LL su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
LL add(LL x){
LL res=0;
for(int y=0;y<(1<<m);y++)
if(!(x&y))res+=s[y];
return res;
}
int main(){
n=read(),m=read()+1,k=read();
for(int i=1;i<=n;i++){
int cnt=read();
while(cnt--){int x=read();a[i]|=(1<<x);}
}
LL nowk=0;
for(int l=1,r=0;l<=n;l++){
if(l>1)s[a[l-1]]--,nowk-=add(a[l-1]);
while(r<n&&nowk+add(a[r+1])<k)
nowk+=add(a[r+1]),r++,s[a[r]]++;
Ans=max(Ans,r-l+1ll);
}
cout<<Ans<<"\n";
return 0;
}
总结
你也许已经发现了用
包裹的都是反思吧。qwq
收获了几条教训:
- 做题之前看清楚数据范围。做题之前看清楚数据范围。做题之前看清楚数据范围。重要的事情说三遍!!!
- 不要太信任自己的直觉,不要栽在一个思路里出不来,如果想到了某些可能有用的性质,一定要顺着常规的套路往下走几步,也许能发现惊喜。
- 不要把题目想的那么难,注意看数据范围,有时候想出来的所谓“暴力”可能就是正解。
行吧。那就这样吧我还能咋样。

浙公网安备 33010602011771号