(体验到了胡出一道题但是写锅的绝望呢)
A:
送分题。
#include<bits/stdc++.h> #define maxn 100005 #define maxm 500005 #define inf 0x7fffffff #define ll long long using namespace std; bool may[30]; char str[maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(){ int T=read(); while(T--){ scanf("%s",str+1); int n=strlen(str+1); memset(may,0,sizeof(may)); for(int i=1;i<=n;){ int j=i; while(str[j]==str[j+1]) j++; if((j-i+1)%2) may[str[i]-'a']=1; i=j+1; } for(int i=0;i<26;i++) if(may[i]) printf("%c",(char)(i+'a')); cout<<endl; } return 0; }
B:
送分题。因为根据样例猜结论wa了一发。
看到有人dp???这难道不是瓶颈在读入的结论题?
#include<bits/stdc++.h> #define maxn 100005 #define maxm 500005 #define inf 0x7fffffff #define ll long long using namespace std; char str[maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(){ int T=read(); while(T--){ int n=read(),n1=0,n0=0,t1=0; for(int i=1;i<=n;i++){ scanf("%s",str+1); int len=strlen(str+1); if(len%2) t1++; for(int j=1;j<=len;j++){ if(str[j]=='1') n1++; else n0++; } } if(n0<n1) swap(n0,n1); if(t1%2){ if(n0%2==n1%2) cout<<n-1<<endl; else cout<<n<<endl; } else if(t1==0){ if(n0%2==1) cout<<n-1<<endl; else cout<<n<<endl; } else cout<<n<<endl; } return 0; }
C:
送分题。
注意到奇数之间的顺序不会变化,偶数之间的顺序也不会变化,于是做一下归并排序的合并操作即可。
#include<bits/stdc++.h> #define maxn 300005 #define maxm 500005 #define inf 0x7fffffff #define ll long long using namespace std; char str[maxn]; int t1[maxn],t0[maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(){ int T=read(); while(T--){ scanf("%s",str+1); int n=strlen(str+1); t1[0]=t0[0]=0; for(int i=1;i<=n;i++){ if((str[i]-'0')%2) t1[++t1[0]]=str[i]-'0'; else t0[++t0[0]]=str[i]-'0'; } int p1=1,p0=1; while(p1<=t1[0] || p0<=t0[0]){ if(p1>t1[0]) printf("%d",t0[p0]),p0++; else if(p0>t0[0]) printf("%d",t1[p1]),p1++; else{ if(t1[p1]<t0[p0]) printf("%d",t1[p1]),p1++; else printf("%d",t0[p0]),p0++; } } cout<<endl; } return 0; }
D:
送分题。
中位数没有太多性质,要么枚举答案要么二分答案。
二分完判一下边界求能达到目标的最小价值即可。
由于没判一个人wa了一发,边界没判好又wa了一发。
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long using namespace std; ll n; struct node{ll l,r;}a1[maxn],a2[maxn],e[maxn]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline bool cmp1(node x,node y){return x.r<y.r;} inline bool cmp2(node x,node y){return x.l<y.l;} inline ll calc(ll x){ ll sum=0,n1=0,n2=0; for(ll i=1;i<=n;i++) if(e[i].r<x) sum+=e[i].l,n1++; for(ll i=1;i<=n;i++) if(e[i].l>x) sum+=e[i].l,n2++; if(n1>n/2) return (1ll<<60); if(n2>n/2) return 0; ll tp=n/2-n1; for(ll i=1;i<=n;i++) if(a1[i].l<=x && a1[i].r>=x){ if(tp) sum+=a1[i].l,tp--; else sum+=x; } return sum; } int main(){ ll T=read(); while(T--){ n=read(); ll s=read(); for(ll i=1;i<=n;i++){ e[i].l=a1[i].l=a2[i].l=read(); e[i].r=a1[i].r=a2[i].r=read(); } sort(a1+1,a1+n+1,cmp2); if(n==1){cout<<min(s,e[1].r)<<endl;continue;} ll l=1,r=(ll)(1e9),ans=0; while(l<=r){ ll mid=l+r>>1; //cout<<mid<<":"<<calc(mid)<<endl; if(calc(mid)<=s) ans=mid,l=mid+1; else r=mid-1; } printf("%I64d\n",ans); } return 0; }
E:
送分题。但我把分还给了这个世界。
实际上所有人被m值划分成了若干个阶梯,每个阶梯都会在一起投票。
考虑$m_{i}$最大的那个人。如果他前面的人数还不到$m_{i}$,那不管怎么样都必须花钱买他,否则暂时没有必要买他。
那么按m值排序后倒着考虑每个人,维护一个以$p_{i}$为关键字的小根堆,如果要买就买若干个最小的。
容易用归纳法证明该算法的正确性。复杂度$O(nlogn)$。
(当时是0:40,我在大脑混乱的情况下想写一个给数组直接排序的$O(n^{2}logn)$的做法过E1。)
(然后我没考虑如果给后面的东西按p排序会把有序的m搞乱掉,于是我就对着一发wa on 2沉思到考试结束。)
(这种明显是我的锅当然要甩给石神了!谁让它那么可爱!)
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long using namespace std; ll n; struct node{ll m,p,id;}a[maxn]; priority_queue<ll> q; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline bool cmp1(node x,node y){return x.m<y.m;} inline bool cmp2(node x,node y){return x.p<y.p;} int main(){ ll T=read(); while(T--){ n=read(); for(ll i=1;i<=n;i++) a[i].m=read(),a[i].p=read(); sort(a+1,a+1+n,cmp1); ll num=0,ans=0; for(ll i=n;i>=1;i--){ q.push(-a[i].p); while(i-1+num<a[i].m) ans+=-q.top(),q.pop(),num++; } printf("%I64d\n",ans); while(!q.empty()) q.pop(); } return 0; }
F:
注意到对于一个红块,它可选的白块范围是确定的。
对于一个$Q_{i}$,如果我们确定了放哪个红块,那么能放的白块数就确定下来了。
那么我们可以预处理每个红块对应的放$i$个白块的方案数。
推组合数复杂度显然不对,但可以考虑生成函数,它的本质也是解决有限或无限元素的组合问题。
如果一个白块的$h_{i}$出现了多于两次,由于要求严格递增,那么它最多只能在两边各放一个。于是我们可以把它当做出现了两次。
根据生成函数相关知识,我们得到:
1.若一个$h_{i}$出现一次,那么它在方案中对应一个$(1+2x)$,表示不放/放左边或放右边。
2.若一个$h_{i}$出现两次,那么它在方案中对应一个$(1+2x+x^{2})$,表示不放/放左边或放右边/放两边。
将这些多项式卷起来,$x^{m}$项的系数就是放m个白块的方案数。
一开始我写了一个卷积快速幂进行$logn$次$NTT$。然后T on 97。(幸好我考场上压根没写)
然后在欧神的教导下我发现:进行一次$NTT$后做点值快速幂就完事了,我根本没学明白。
一些非常致命的细节:
1.写多项式应用题时一定要开一个临时数组然后对其$NTT$,不然会将原数组变换成点值表示。
2.如果你不想开临时数组可以每次把用到的数组清空一遍,注意清空一定要清到$NTT$时的len而不是需要保留的len。
3.强烈不建议$NTT$过去后再$NTT$回来,1s跑不了几次$NTT$。
#include<bits/stdc++.h> #define maxn 300005 #define maxm 1<<21|1 #define inf 0x7fffffff #define mod 998244353 #define g 3 #define ll long long #define rint register ll using namespace std; ll mx=3e5,h1[maxn],h2[maxn],ind[maxm]; ll p1[maxm],p2[maxm],tq[maxm],tp[maxm]; ll res[5][maxm]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline ll mo(ll x){return x>=mod?x-mod:x;} inline ll power(ll a,ll b){ll ans=1;while(b){ans=(b&1)?ans*a%mod:ans,a=a*a%mod,b>>=1;}return ans;} inline void copy(ll *a,ll *b){for(rint i=0;i<(maxm>>1);i++) a[i]=b[i];} inline void clear(ll *a,ll x){for(rint i=0;i<(maxm>>1);i++) a[i]=0;a[0]=1,a[1]=(x?2:0),a[2]=(x==2);} //不能使用sizeof(*a)的形式 inline void print(ll *a){for(rint i=0;i<=10;i++) cout<<a[i]<<" ";cout<<endl;} namespace P{ inline void ntt(ll *a,ll n,ll op){ for(rint i=0;i<n;i++) ind[i]=(i&1)?((ind[i>>1]>>1)|(n>>1)):(ind[i>>1]>>1); for(rint i=0;i<n;i++) if(ind[i]>i) swap(a[i],a[ind[i]]); for(rint l=2;l<=n;l<<=1){ ll p=power(g,(mod-1)/l); if(op==-1) p=power(p,mod-2); for(rint i=0;i<n;i+=l) for(ll j=i,w=1,t;j<i+(l>>1);j++,w=w*p%mod) t=a[j+(l>>1)]*w%mod,a[j+(l>>1)]=mo(a[j]-t+mod),a[j]=mo(a[j]+t); } } inline ll conv(ll *a,ll n,ll *b,ll m){ ll len=1,lim=min(n+m,mx); while(len<=lim) len<<=1; ntt(a,len,1),ntt(b,len,1); for(rint i=0;i<len;i++) a[i]=a[i]*b[i]%mod; ntt(a,len,-1); ll inv=power(len,mod-2); for(rint i=0;i<len;i++) a[i]=a[i]*inv%mod; return lim; } inline ll conp(ll *a,ll b,ll c){ if(b==0){clear(a,0);return 0;} ll len=1,lim=min(c*b,(ll)mx); while(len<=lim) len<<=1; ntt(a,len,1); for(rint i=0;i<len;i++) a[i]=power(a[i],b); ntt(a,len,-1); ll inv=power(len,mod-2); for(rint i=0;i<len;i++) a[i]=a[i]*inv%mod; return lim; } } int main(){ ll n=read(),k=read(); for(rint i=1;i<=n;i++) h1[i]=read(); for(rint i=0;i<k;i++) h2[i]=read(); sort(h1+1,h1+1+n),sort(h2,h2+k); for(rint i=0;i<k;i++){ ll l1=0,l2=0,now=1,n1=0,n2=0; while(now<=n){ if(h1[now]>=h2[i]) break; ll j=now; while(j+1<=n && h1[j]==h1[j+1]) j++; if(j==now) n1++; else n2++; now=j+1; } clear(p1,1),l1=1;l1=P::conp(p1,n1,l1); clear(p2,2),l2=2;l2=P::conp(p2,n2,l2); //print(p1),print(p2); l1=P::conv(p1,l1,p2,l2),copy(res[i],p1); //print(res[i]); } ll Q=read(); while(Q--){ ll q=read(),ans=0; for(rint i=0;i<k;i++){ ll num=(q-2*h2[i])/2; if(num-1<0) break; if(num-1>(ll)mx) continue; ans=mo(ans+res[i][num-1]); } printf("%I64d\n",ans); } return 0; }