(D1T2) 国王游戏 ((OK))
(D2T1) 同余方程 ((OK))
(D2T2) 借教室 ((OK))
因为最近时间比较紧,所以紫题一律跳过,等最后一个星期不联考的时候再回来填坑.
(D1T1)就字符串模拟题,刚开始看错题了,题目给的是密钥和密文,要求明文.注意区分一下大小写.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
char s1[1005],s2[1005];
char b[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
char a[26]={'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
int main(){
scanf("%s%s",s1+1,s2+1);
int k=strlen(s1+1),n=strlen(s2+1);
for(int i=1;i<=k;++i)//把密钥补全
for(int j=1;i+j*k<=n;++j)s1[i+j*k]=s1[i];
for(int i=1;i<=n;++i){
if(s2[i]>='A'&&s2[i]<='Z'){
if(s1[i]>='A'&&s1[i]<='Z'){
int x=s2[i]-'A'-(s1[i]-'A');
x+=26;printf("%c",a[x%26]);
}
if(s1[i]>='a'&&s1[i]<='z'){
int x=s2[i]-'A'-(s1[i]-'a');
x+=26;printf("%c",a[x%26]);
}
}
if(s2[i]>='a'&&s2[i]<='z'){
if(s1[i]>='A'&&s1[i]<='Z'){
int x=s2[i]-'a'-(s1[i]-'A');
x+=26;printf("%c",b[x%26]);
}
if(s1[i]>='a'&&s1[i]<='z'){
int x=s2[i]-'a'-(s1[i]-'a');
x+=26;printf("%c",b[x%26]);
}
}
}
printf("
");return 0;
}
(D1T2)恩,很经典的贪心题了,见过好多次了.本题结论是通过贪心证明方法(---)邻项微扰法证的.因为后面(40)分是高精,所以我懒得再写代码了,就口胡一下证明过程吧.
假设最优方案中有两位大臣(i)和(i+1),它们之前((1)~(i-1))位大臣左手乘积为(sum).那么此时产生的贡献是(max(frac{sum}{b_i},frac{sum*a_i}{b_{i+1}})),如果交换这两位大臣,那么产生的贡献是(max(frac{sum}{b_{i+1}},frac{sum*a_{i+1}}{b_{i}})).为了下文表示方便,按照出场顺序我们把这四个整数依次记为(k_1,k_2,k_3,k_4).
首先根据式子显然有(k_1<k_4,k_2>k_3),因为前面那种是最优方案,所以(max(k_1,k_2)<max(k_3,k_4)),那么可以推得(k_2<k_4.)
即(frac{sum*a_i}{b_{i+1}}<frac{sum*a_{i+1}}{b_{i}})
所以有(sum*a_i*b_i<sum*a_{i+1}*b_{i+1}),同时约掉(sum)
即(a_i*b_i<a_{i+1}*b_{i+1})
所以本题的贪心策略就是按照左手右手的乘积从小到大排序.
(D1T3)咕咕咕.
(D2T1)最近考过两次了,扩欧的模板题.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline ll read(){
ll x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
ll a,b,x,y;
inline void exgcd(ll a,ll b,ll &x,ll &y){
if(!b){x=1;y=0;return;}
exgcd(b,a%b,x,y);
ll z=x;x=y;y=z-y*(a/b);
}
int main(){
a=read();b=read();exgcd(a,b,x,y);
printf("%lld
",(x%b+b)%b);
return 0;
}
(D2T2),刚开始看到这道题其实是看错题了,以为是要求出所有不能满足要求的请求.然后看这些操作其实就是线段树的区间查询最小值和区间增加(负数),所以直接写的线段树模板,当然在看清题目后发现还是可以用线段树写啊.题目只要求第一个不能被满足的请求.
然后发现这样的话,还可以二分答案(mid),这时只要检查是否有教室的前缀和大于该教室给定的那个数即可.因为如果第(mid)个请求时,有一个教室的前缀和大于该教室给定的那个数,那么第(mid+1)个请求时,那个教室的前缀和只会更大.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=1e6+5;
int n,m,ask_min;
int tot[N],minn[N<<2],add[N<<2];
inline void build(int p,int l,int r){
if(l==r){minn[p]=tot[l];add[p]=0;return;}
int mid=(l+r)>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
minn[p]=min(minn[p<<1],minn[p<<1|1]);
}
inline void pushdown(int p,int l,int r,int mid){
if(!add[p])return;
add[p<<1]+=add[p];add[p<<1|1]+=add[p];
minn[p<<1]+=add[p];minn[p<<1|1]+=add[p];
add[p]=0;
}
inline void ask(int p,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r){ask_min=min(ask_min,minn[p]);return;}
int mid=(l+r)>>1;pushdown(p,l,r,mid);
if(ql<=mid)ask(p<<1,l,mid,ql,qr);
if(qr>mid)ask(p<<1|1,mid+1,r,ql,qr);
}
inline void change(int p,int l,int r,int ql,int qr,int val){
if(ql<=l&&qr>=r){add[p]+=val;minn[p]+=val;return;}
int mid=(l+r)>>1;pushdown(p,l,r,mid);
if(ql<=mid)change(p<<1,l,mid,ql,qr,val);
if(qr>mid)change(p<<1|1,mid+1,r,ql,qr,val);
minn[p]=min(minn[p<<1],minn[p<<1|1]);
}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i)tot[i]=read();
build(1,1,n);
for(int i=1;i<=m;++i){
int z=read(),x=read(),y=read();
ask_min=1<<30;ask(1,1,n,x,y);
if(ask_min<z){printf("-1
%d
",i);return 0;}
change(1,1,n,x,y,-z);
}
puts("0");
return 0;
}
二分答案当然全方位(时空复杂度,码量)吊打线段树:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*o;
}
const int N=1e6+5;
int n,m,x[N],y[N],z[N],tot[N],sum[N];
inline bool check(int mid){
for(int i=1;i<=n;++i)sum[i]=0;
for(int i=1;i<=mid;++i)sum[x[i]]+=z[i],sum[y[i]+1]-=z[i];
int now=0;
for(int i=1;i<=n;++i){
now+=sum[i];
if(now>tot[i])return false;
}
return true;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i)tot[i]=read();
for(int i=1;i<=m;++i)z[i]=read(),x[i]=read(),y[i]=read();
int l=0,r=n,mid,ans;
while(l<=r){
mid=(l+r)>>1;
if(check(mid))ans=mid,l=mid+1;
else r=mid-1;
}
if(ans==n){puts("0");return 0;}
printf("-1
%d
",ans+1);
return 0;
}