(第一把div1心态崩了,给大家表演了一把上蓝)
(看来以后div1需要先读前三题,如果没把握切掉还是不要交了……)
A:
题意是求最少用几个形如$2^{t}+p$的数拼出n,给定n和p。$nleq 10^{9},-1000leq pleq 1000,kgeq 0$。
我们不妨考虑如何判断一个k是否能成为答案。显然移项之后变成了用$2^{t}$拼$n-kp$。
设$n-kp$的二进制中有a个1,拼出它所用的$2^{t}$个数为num,那么$num_{min}=a$。
注意到我们可以把这a个中的某些$2^{t}$拆成两个$2^{t-1}$,最多可以拆出$n-kp$个(全用1拼)。
感性理解一下,显然num的可能取值是连续的。那么只要判断k是否在$[a,n+kp]$之间即可。
那我们应该枚举k到多少呢?注意到$a_{max}=log{n}$,那么只要k枚举到$log{n}$一定能找到解或者判断无解。
这么一个送分题我进入网页花了5分钟,想了5分钟,写了5分钟,我真是活该掉分。
#include<bits/stdc++.h> #define maxn 100005 #define maxm 500005 #define inf 0x7fffffff #define ll long long using namespace std; 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; } inline int calc(int x){int cnt=0;while(x){cnt+=(x&1);x>>=1;}return cnt;} int main(){ int n=read(),p=read(); if(n==p){printf("-1 ");return 0;} for(int i=1;i<=100;i++){ n-=p; if(n<i){printf("%d ",-1);return 0;} if(calc(n)<=i && i<=n){printf("%d ",i);return 0;} } return 0; }
B:
题意是求有多少对$i,j$满足$a_{i}cdot a_{j}=x^{k}$。$nleq 10^{5},2leq kleq 100$。
看到这题大概是个人都有一个想法:维护一个质因数与指数集合的集合。
每次把$a_{i}$质因数分解,然后指数膜k。若膜k得0则忽略该质因数。
最后查找有多少数能和它的指数匹配(加起来膜k得0),并向集合中插入该数。
然后我不知道怎么写这个东西好写,于是去想了一些高论做法。
然而太菜什么都没想出来,只好手写这个东西,然后re on 6。
我:???
你wa on 6我也可以调,你tm的re on 6又不给result,我拿头调???
然后我陆续交了几发仍然re,于是心态崩了,去打炉石。(一套海盗战从10砍到了6,强烈推荐)
比完之后我点开第一个A的code,发现他写了一个这样的东西。
map<vector<pair<int,int> >,int> ans;
我:???
积累一个实用小知识:打cf一定要把stl的所有操作学好,不然你re都不知道是怎么re的。
#include<bits/stdc++.h> #define maxn 100005 #define maxm 500005 #define inf 0x7fffffff #define ll long long using namespace std; map<vector<pair<int,int> >,int> res; 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 n=read(),k=read(); ll ans=0; for(int nu=1;nu<=n;nu++){ int x=read(); vector<pair<int,int> > a,b; //a.push_back(0),b.push_back(0); for(int j=2;j<=sqrt(x);j++){ int p=0; while(x%j==0) x/=j,p++; p%=k; if(p==0) continue; a.push_back(make_pair(j,p)); b.push_back(make_pair(j,k-p)); } if(x!=1){ a.push_back(make_pair(x,1)); b.push_back(make_pair(x,k-1)); } ans+=(ll)res[b],res[a]++; } cout<<ans<<endl; return 0; }
C:
由于上述原因(海盗战txdy)我并没有看这道题。今天上午胡了一下还胡错了,我真是活该掉分。
题意大概是给你一个$n imes m$的地图,每个点要么是空的要么有石头。
你只能向下或向右走,如果你撞到一个石头会把它推着一起走。
如果推到地图边界就不能再推了,也就是不能再朝该方向走。
求有多少种方法从$(1,1)$走到$(n,m)$。$n,mleq 2000$。
这种有限制的移动题一般还是考虑用dp去表示它的状态以及走法。
我们如果考虑从$(1,1)$走到$(i,j)$比较麻烦,因为没法表示有多少石头的状态。
那如果倒着考虑,从$(i,j)$走到$(n,m)$呢?好多了,可以通过石头个数判断后面能走哪。但由于不知道走的方向比较麻烦。
考虑一条路径,我们如果只在它转弯的地方进行dp转移,就变得方便很多。
那么可以考虑设$D_{i,j},R_{i,j}$。其中$D_{i,j}$表示现在在$(i,j)$,下一步要往下,上一步不是往下的方案数。
设$(i,j)$正下方有k个石头,那么最多走$n-k-i$步就必须右转了。于是有$D_{i,j}=sum_{s=1}^{n-k-i}{R_{i+s,j}}$。
R的转移类似。我写了个$O(nmlogn)$的,其实记一下石头个数完全可以$O(nm)$。
#include<bits/stdc++.h> #define maxn 2005 #define maxm 500005 #define inf 0x7fffffff #define mod 1000000007 #define ll long long using namespace std; char mp[maxn][maxn]; ll n,m,d[maxn][maxn],r[maxn][maxn]; ll cd[maxn][maxn],cr[maxn][maxn]; ll nd[maxn][maxn],nr[maxn][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 ll mo(ll &x){return x>=mod?x-mod:x;} inline ll lowbit(ll x){return x&(-x);} inline ll sr(ll t,ll x){ll ans=0;for(ll i=x;i;i-=lowbit(i)) mo(ans+=cr[t][i]);return ans;} inline ll sd(ll t,ll x){ll ans=0;for(ll i=x;i;i-=lowbit(i)) mo(ans+=cd[t][i]);return ans;} inline ll qr(ll t,ll x,ll y){return (sr(t,y)-sr(t,x-1)+mod)%mod;} inline ll qd(ll t,ll x,ll y){return (sd(t,y)-sd(t,x-1)+mod)%mod;} inline void ar(ll t,ll x,ll y){for(ll i=x;i<=n;i+=lowbit(i)) mo(cr[t][i]+=y);} inline void ad(ll t,ll x,ll y){for(ll i=x;i<=m;i+=lowbit(i)) mo(cd[t][i]+=y);} int main(){ n=read(),m=read(); for(ll i=1;i<=n;i++) scanf("%s",mp[i]+1); for(ll i=1;i<=n;i++) for(ll j=m;j>=1;j--) nr[i][j]=nr[i][j+1]+(mp[i][j]=='R'); for(ll i=n;i>=1;i--) for(ll j=1;j<=m;j++) nd[i][j]=nd[i+1][j]+(mp[i][j]=='R'); d[n][m]=r[n][m]=1,ad(n,m,1),ar(m,n,1); for(ll i=n;i>=1;i--){ for(ll j=m;j>=1;j--){ if(i==n && j==m) continue; ll t1=nd[i+1][j],t2=nr[i][j+1]; if(i+1<=n-t1) d[i][j]=qr(j,i+1,n-t1); if(j+1<=m-t2) r[i][j]=qd(i,j+1,m-t2); ad(i,j,d[i][j]),ar(j,i,r[i][j]); //cout<<i<<" "<<j<<":"<<d[i][j]<<" "<<r[i][j]<<endl; } } if(n==m && n==1) cout<<(mp[1][1]=='.')<<endl; else cout<<(d[1][1]+r[1][1])%mod<<endl; return 0; }
D:
咕了,补别的题(玩海盗战)。