Cyber Language
可以用stringstream
每次得到一个单词
inline void solve(){
string s; getline(cin,s);
stringstream ss; ss<<s;
string str;
while(ss>>str){
char t=str[0];
t=t-'a'+'A';
cout<<t;
}
cout<<endl;
}
Package Delivery(贪心)
一开始当成区间覆盖做了,发现用一种情况不能被考虑到
按照区间覆盖排完序之后,因为是按照右端点递增排序,所以会有这种情况发生:
[1,4] [5,6] [2,8]
一开始选择了4这个点,但是到第二个区间就会转移到6这个点,第三个区间可以放在4这个点,所以就会导致答案变差
可以将每次取得点存进set中,若选则的点与当前区间为无交集,则插入一个新的点,否则在set中找到第一个大于等于左区间的点拿快递一定更优
注意特判k=1的情况,因为我是先插入了一个点,后面会先++再判断是否等于k(感谢恒哥!)
const int N=1e6+10;
int n,m;
PII g[N];
bool cmp(PII a,PII b){
if(a.r==b.r) return a.l<b.l;
return a.r<b.r;
}
inline void solve(){
cin>>n>>m;
for(int i=0;i<n;i++){
int a,b; cin>>a>>b;
g[i]={a,b};
}
if(m==1) cout<<n<<endl;
else{
sort(g,g+n,cmp);
//for(int i=0;i<n;i++) cout<<g[i].l<<' '<<g[i].r<<endl;
set<int>s;map<int,int>mp;
s.insert(0);
int ans=1,pos=g[0].r;
mp[pos]=1,s.insert(pos);
for(int i=1;i<n;i++){
// cout<<g[i].l<<' '<<g[i].r<<' '<<pos<<' '<<cnt<<' '<<ans<<endl;
if(g[i].l>*(--s.end())){
pos=g[i].r;
ans++;
mp[pos]=1,s.insert(pos);
}
else{//g[i].l<=pos
int now=*s.lower_bound(g[i].l);
if(now<=g[i].r){
mp[now]++;
if(mp[now]==m){
mp.erase(now);
s.erase(now);
}
}
else{
pos=g[i].r;
ans++;
mp[pos]=1,s.insert(pos);
}
}
}
cout<<ans<<endl;
}
Two Permutations(dp)
f[i][j]
表示在s中凑到第i位,第i位选P/Q中的方案数(学习自恒哥)
const int N=3e5+10;
int n;
int a[N],b[N],s[2*N];
int posa[N],posb[N];
int f[2*N][3];
inline void solve(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],posa[a[i]]=i;
for(int i=1;i<=n;i++) cin>>b[i],posb[b[i]]=i;
for(int i=1;i<=2*n;i++) cin>>s[i];
if(a[1]==s[1]) f[1][0]=1;
else f[1][0]=0;
if(b[1]==s[1]) f[1][1]=1;
else f[1][1]=0;
for(int i=2;i<=2*n;i++){
f[i][1]=f[i][0]=0;
int last=posa[s[i-1]];
if(last>=1&&last<n&&a[last]==s[i-1]&&a[last+1]==s[i]){
f[i][0]+=f[i-1][0];
}
last=i-posb[s[i-1]];
if(last>=1&&last<=n&&a[last]==s[i]){
f[i][0]+=f[i-1][1];
}
last=posb[s[i-1]];
if(last>=1&&last<n&&b[last]==s[i-1]&&b[last+1]==s[i]){
f[i][1]+=f[i-1][1];
}
last=i-posa[s[i-1]];
if(b[last]==s[i]){
f[i][1]+=f[i-1][0];
}
f[i][1]%=mod;
f[i][0]%=mod;
}
cout<<(f[2*n][1]+f[2*n][0])%mod<<endl;
}
Boss Rush(二分+状压dp+记忆化搜索)
物品数较小,可以用状压枚举出所有情况
找最小时间二分
不能枚举出每种技能的使用先后顺序,所以可以用记忆化搜索求出每种情况下造成的最大伤害
const int N=20,M=1e5+10;
int n,m,mid,l,r;
int f[1<<N];
int s[N][M],t[N],len[N];
int dfs(int u){
if(f[u]!=-1) return f[u];//剪枝
int time=0,res=0;
for(int i=0;i<n;i++){
if(u>>i&1) time+=t[i+1];
}
if(time>mid) return f[u]=0;//mid不能小于总冷却时间
for(int i=0;i<n;i++){
if(!(u>>i&1)){
res=max(res,s[i+1][min(len[i+1],mid-time)]+dfs(u|(1<<i)));//记录最大伤害
}
}
return f[u]=res;
}
bool check(){
for(int i=0;i<(1<<n);i++) f[i]=-1;
return dfs(0)>=m;
}
inline void solve(){
cin>>n>>m;
l=r=0;
for(int i=1;i<=n;i++){
cin>>t[i]>>len[i];
r+=max(t[i],len[i]);
for(int j=1;j<=len[i];j++) cin>>s[i][j],s[i][j]+=s[i][j-1];
}
int ans=inf;
while(l<=r){
mid=l+r>>1;
if(check()) ans=min(ans,mid),r=mid-1;
else l=mid+1;
}
if(ans==inf) cout<<-1<<endl;
else cout<<ans-1<<endl;
}
Taxi(二分)
为满足二段性,应先对w进行排序
我们可以利用上述公式O(1)求出排序后的所有点与(x,y)的最大距离dist
可以二分到达哪个点mid能得到最大的优惠
当 d i s t [ m i d , n ] > w m i d dist_{[mid,n]}>w_{mid} dist[mid,n]>wmid时,最大优惠是 w m i d w_{mid} wmid,向右二分
否则向左二分
const int N=1e5+10;
int n,m;
int a[N],b[N],c[N],d[N];
struct P{
int x,y,w;
bool operator<(const P&t)const{
return w<t.w;
}
}p[N];
int ans,x,y;
bool check(int mid){
int dist=0;
dist=max(dist,x+y+a[mid]);
dist=max(dist,x-y+b[mid]);
dist=max(dist,-x+y+c[mid]);
dist=max(dist,-x-y+d[mid]);
ans=max(ans,min(p[mid].w,dist));
return dist>=p[mid].w;
}
inline void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int x,y,z; cin>>x>>y>>z;
p[i]={x,y,z};
}
sort(p+1,p+n+1);
a[n+1]=b[n+1]=c[n+1]=d[n+1]=-inf;
for(int i=n;i>=1;i--){
int x=p[i].x,y=p[i].y;
a[i]=max(a[i+1],-x-y);
b[i]=max(b[i+1],-x+y);
c[i]=max(c[i+1],x-y);
d[i]=max(d[i+1],x+y);
}
while(m--){
cin>>x>>y;
int l=1,r=n;ans=0;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) l=mid+1;
else r=mid-1;
}
cout<<ans<<endl;
}
}