突然发现博客咕了好久了,今天更新一下~~
A. Anti-knapsack
给定整数 (n,k),从({1,2,3,cdots,n}) 中选出最大的子集 (S),使得 ( exists Asubseteq S,sumlimits_{x in A}x=k)。
显然,(gt k) 的随便选,(lt k) 的选 (lceilfrac{k}{2} ceil,lceilfrac{k}{2} ceil+1,cdots,k-1) 即可。可以从可行性及最优性两方面证明。
Code:(考场写的,比较潦草)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read(){
T x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define rdi read<int>
#define rdl read<ll>
int n,k,T;
int main(){
T=rdi();
while(T--){
n=rdi(),k=rdi();
int cnt=0,a[1010]={0};
for(int i=k+1;i<=n;i++) a[++cnt]=i;
for(int i=(k+1)/2;i<k;i++) a[++cnt]=i;
printf("%d
",cnt);
for(int i=1;i<=cnt;i++) printf("%d ",a[i]);
puts("");
}
return 0;
}
B. Planet Lapituletti
假设一天有 (h) 小时,每小时 (m) 分钟。给定一个起始时间 (HH:MM形式),求从起始时间起第一个合法的镜像时间。
合法时间:该时间以数位管形式显示时,(0 le 分钟数 lt m),(0 le 时钟数 lt m)。
合法镜像时间:该时间是合法时间,且该时间在镜子中投影(即左右翻转)后仍可以作为一个合法时间。
打表出 (0sim 9) 对应镜像数字的表(不合法为 (-1)),然后枚举时间即可。
一个坑:(00:01) 可以到 (00:00)(相当于到了第二天)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<typename T>
inline T read(){
T x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define rdi read<int>
#define rdl read<ll>
int T,n,m,x,y;
const int fx[]={0,1,5,-1,-1,2,-1,-1,8,-1};
pii rev(int x,int y){
int a=x/10,b=x%10,c=y/10,d=y%10;
if(fx[a]==-1||fx[b]==-1||fx[c]==-1||fx[d]==-1) return make_pair(-1,-1);
return make_pair(fx[d]*10+fx[c],fx[a]+fx[b]*10);
}
int main(){
T=rdi();
while(T--){
n=rdi(),m=rdi();
scanf("%d:%d",&x,&y);
for(;;y++,(x+=y/m)%=n,y%=m){
if(rev(x,y).first==-1) continue;
if(rev(x,y).first<n&&rev(x,y).second<m){
printf("%02d:%02d
",x,y);
break;
}
}
}
return 0;
}
C. K-beautiful Strings
给定一个长度为 (n) 的字符串 (s) 及一个常数 (k),求字典序大于等于 (s) 、长度为 (n) 的字典序最小的好的字符串。
好的字符串:由小写字母构成,且每种字符都出现了 (k) 的倍数次。
既然要求字典序大于等于 (s) 且字典序最小,那么可以从后往前枚举前缀。
假设当前枚举到了 (i),先从 (s_i) 开始枚举当前这一位取什么字符,然后统计 (s_{1dots i}) 中每种字符的出现次数,设为 (cnt),那么显然,后面 (s_{i+1dots n}) 要补充 (sum=sumlimits_{i='a'}^{'z'} ((k-cnt_imod k)mod k)) 个字符,此时存在合法字符串,当且仅当 (sum le n-i) 且 $ sum equiv n-i pmod k$。如果满足,我们就先填充 (sum-(n-i)) 个字符 ‘a’ ,然后从 ‘a’ 到 ‘z’ 依次处理当前字符需填充数量并填充即可。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read(){
T x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define rdi read<int>
#define rdl read<ll>
const int N=100010;
int T,n,k;
char s[N];
int main(){
T=rdi();
while(T--){
n=rdi(),k=rdi(),scanf("%s",s+1);
if(n%k){
puts("-1");
continue;
}
int cnt[30]={0};
for(int i=1;i<=n;i++) cnt[s[i]-'a']++;
bool flg=1;
for(int j=0;j<26;j++) flg&=((cnt[j]%k)==0);
if(flg){
printf("%s
",s+1);
continue;
}
for(int i=n;i>=1;i--){
int cur=s[i]-'a',success=0;
for(int j=cur+1;j<=26;j++){
cnt[cur]--;cnt[j]++;s[i]=j+'a';
int kkk=0;
for(int j=0;j<26;j++){
if(cnt[j]%k) kkk+=(k-cnt[j]%k)%k;
}
if(kkk<=n-i&&(n-i-kkk)%k==0){
int pos=i;
for(int o=1;o<=(n-i-kkk)-(n-i-kkk)%k;o++) s[++pos]='a';
for(int o=0;o<26;o++){
if(cnt[o]%k){
for(int p=1;p<=(k-cnt[o]%k);p++) s[++pos]=o+'a';
}
}
success=1;
goto ed;
}
cnt[cur]++,cnt[j]--;s[i]=cur+'a';
}
cnt[s[i]-'a']--;
}
// char cur=s[0]+1;
// for(int i=1;i<=k;i++) s[i]=
ed:
printf("%s
",s+1);
}
return 0;
}
D. CF1493D GCD of an Array
给定一个序列 (a),要求支持每次将某个位置 (i) 乘上一个数并询问当前整个序列的 (gcd)。
先筛一下质数,开一些 (multiset) ,第 (i) 个维护 (a) 中每个数分解质因数后 (1sim 2 imes10^5) 中第 (i) 大的质数的指数。
修改将要乘的数分解质因数后直接加上即可。但查询若把 (1sim 2 imes10^5) 每个质数都查询一次最小值肯定会TLE,所以要维护答案的变化量,只查询被修改过的对应的质数。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<typename T>
inline T read(){
T x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define rdi read<int>
#define rdl read<ll>
const int N=200010,MOD=1e9+7,INF=0x3f3f3f3f,P=18010;
int n,q,a[N];
int p[N],cp,vis[N];
void pr(int lim){
vis[0]=vis[1]=1;
for(int i=2;i<=lim;i++){
if(!vis[i]){
p[++cp]=i;
for(int j=i;j<=lim/i;j++) vis[i*j]=1;
}
}
}
ll qpow(ll x,ll y){
ll res=1;
while(y){
if(y&1) (res*=x)%=MOD;
(x*=x)%=MOD;y>>=1;
}
return res;
}
multiset<int> s[P];
map<int,int> fac[N],tmp;
ll ans=1;
ll gcd(ll x,ll y){return !x?y:gcd(y%x,x);}
int main(){
n=rdi(),q=rdi();
for(int i=1;i<=n;i++){
a[i]=rdi();
if(i==1) ans=a[i];
else ans=gcd(ans,a[i]);
}
pr(N-10);
for(int i=1;i<=n;i++){
int lim=sqrt(a[i]),x=a[i];
for(int j=1;p[j]<=lim&&j<=cp;j++){
if(x%p[j]==0){
int cnt=0;
while(x%p[j]==0) x/=p[j],cnt++;
fac[i][j]=cnt;
}
lim=sqrt(x);
}
if(x>1){
int tmp=lower_bound(p+1,p+cp+1,x)-p;
fac[i][tmp]++;
}
for(auto x:fac[i]) s[x.first].insert(x.second);
}
while(q--){
int id=rdi(),val=rdi();
int lim=sqrt(val),x=val;tmp.clear();
for(int j=1;p[j]<=lim&&j<=cp;j++){
if(x%p[j]==0){
int cnt=0;
while(x%p[j]==0) x/=p[j],cnt++;
tmp[j]=cnt;
}
lim=sqrt(x);
}
if(x>1){
int posss=lower_bound(p+1,p+cp+1,x)-p;
tmp[posss]++;
}
for(auto x:tmp){
int x1=x.first,x2=x.second;
int pre=fac[id][x1],post=pre+x2;
fac[id][x1]+=x2;
int minx1=s[x1].size()<n?0:(*s[x1].begin());
if(pre) s[x1].erase(s[x1].find(pre));
s[x1].insert(post);
int minx2=s[x1].size()<n?0:(*s[x1].begin());
ans=ans*qpow(p[x1],minx2-minx1)%MOD;
}
printf("%lld
",ans);
}
return 0;
}
完结撒花~~