T1 lgg
L 君和 G 君在玩一个游戏。G 君写下一个字符串 A,L 君将其复制一遍连接到 A 串后面得
到字符串 B, G 君又在 B 的任意位置(包括首尾)插入一个字符得到字符串 C。现在你得到了 C,
请你找出 A。
第一行一个整数
T
T
T,表示有
T
T
T 组数据
每组数据第一行一个整数
n
n
n,表示字符串
C
C
C 的长度。
每组数据第二行
n
n
n 个大写字母,表示字符串
C
C
C。
若不存在可能的字符串
A
A
A,输出一行
“
N
O
T
P
O
S
S
I
B
L
E
”
“NOT\ POSSIBLE”
“NOT POSSIBLE” ;若存在多个不同的字符串
A
A
A 满
足条件,输出一行
“
N
O
T
U
N
I
Q
U
E
”
“NOT\ UNIQUE”
“NOT UNIQUE” ;否则输出一行为满足条件的字符串
A
A
A。
solution:
裸的哈希,然后用 m a p map map判一下重就好了,考场眼瞎没看见不同的字符串,直接 0 0 0分 q n m d qnmd qnmd
放 A C AC AC代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define maxn 1000005
#define int long long
using namespace std;
int t,n,has[maxn],pw[maxn];
const int bas=127,mod=1e9+7;
char s[maxn];
map<int,int> mp;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
inline void prework(){
pw[0]=1;
for(int i=1;i<=1000001;i++) pw[i]=pw[i-1]*bas%mod;
}
signed main(){
freopen("lgg.in","r",stdin);
freopen("lgg.out","w",stdout);
t=rd(); prework();
while(t--){
n=rd(); scanf("%s",s+1);
if(!(n&1) || n==1) {puts("NOT POSSIBLE");continue;}
for(int i=1;i<=n;i++){
has[i]=(has[i-1]*bas%mod+s[i]-'A'+1)%mod;
}
int pos,ans=0,tmp1,tmp2,tar=0; mp.clear();
for(int i=1;i<=n;i++){//枚举删除
if(i<=n/2+1){
pos=(n+1)>>1;
tmp1=has[pos]-has[i]*pw[pos-i]%mod;
tmp1=((tmp1%mod+mod)%mod+has[i-1]*pw[pos-i]%mod)%mod;
tmp2=has[n]-has[pos]*pw[n-pos]%mod;
tmp2=(tmp2%mod+mod)%mod;
if(tmp1==tmp2){
if(!ans) ans=1,tar=i;
else if(ans==1 && mp[tmp1]==0) ans=2;
mp[tmp1]=1;
}
}
else{
pos=(n-1)>>1;
tmp1=has[pos];
tmp2=has[n]-has[i]*pw[n-i]%mod;
tmp2=((tmp2%mod+mod)%mod+(has[i-1]-has[pos]*pw[i-1-pos]%mod+mod)*pw[n-i]%mod)%mod;
if(tmp1==tmp2){
if(!ans) ans=1;
else if(ans==1 && mp[tmp1]==0) ans=2;
mp[tmp1]=1;
}
}
if(ans==2) break;
}
if(!ans) puts("NOT POSSIBLE");
else if(ans==2) puts("NOT UNIQUE");
else{
int len=0;
for(int i=1;i<=n;i++){
if(i!=tar) printf("%c",s[i]),len++;
if(len==(n-1)>>1) break;
}
puts("");
}
}
return 0;
}
T2 k
小
Y
Y
Y 最近加冕为西班牙皇帝了! 小
Y
Y
Y 想跟你分享他多年奋斗的人生经验,虽然你表示完全不想理他,但还是被拖了过去。
具体来说,小
Y
Y
Y 有一个威望值的属性,初始为
1
1
1,为了成为皇帝,他需要达到
K
K
K 的威 望值。获得威望值的方法如下:假设小
Y
Y
Y 现在的威望值为
W
W
W,那么小
Y
Y
Y 每次会宣称一个 数
V
V
V ,满足
W
⋅
V
∣
K
W ·V|K
W⋅V∣K,之后付出
f
(
V
)
f(V )
f(V) 的代价,并将
W
W
W 变成
W
⋅
V
W ·V
W⋅V 。当
W
=
K
W = K
W=K 时,小
Y
Y
Y 就胜利了。
f
(
V
)
f(V )
f(V) 是攻下
V
V
V 的代价,定义
p
p
p 为
V
V
V 的十进制各位数字之和加上
5
5
5,
q
q
q 为
V
V
V 的十进制 各位数字之积加上
233
233
233,
S
S
S 为
V
V
V 的质因子集合。每次可以付出
10
10
10 的代价使
q
q
q 变成
q
+
1
q + 1
q+1, 或者选定
x
∈
S
x ∈ S
x∈S 并付出
1
1
1 的代价使
q
q
q 变成
q
⋅
x
q·x
q⋅x,直到
p
∣
q
p|q
p∣q,完成这个过程所需的最小代价 就是
f
(
V
)
f(V )
f(V)。 这实在是太复杂了,你深刻地体会到皇帝不好当,现在你只想知道成为皇帝的最小代 价是多少。 特别地,本题中
∣
µ
(
K
)
∣
=
1
|µ(K)| = 1
∣µ(K)∣=1,且为了方便,
K
K
K 的最大质因子
≤
1
0
6
≤ 10^6
≤106。
solution:
考试的时候眼瞎看错样例
因为 ∣ µ ( K ) ∣ = 1 |µ(K)| = 1 ∣µ(K)∣=1,所以它一定是许多质数的乘积,并且没有平方因子,因为这个性质,可以发现 K K K最多有 15 15 15个质因子,所以就可以状压,考虑枚举子集,然后分成两部分那样算,但枚举子集是 3 n 3^n 3n,所以要 O ( 1 ) O(1) O(1)转移,可以先 2 n × n 2^n\times n 2n×n预处理,算出当 V V V是某个质数集合的时候的 f ( V ) f(V) f(V),这个可以按照图论的思想,跑最短路那样算,这个没有想到,应该是很常用的方法了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
LL K,f[(1<<16)+5],pri[20],n,g[(1<<16)+5],dis[185];
int cnt,head[185],sss[20],syq;
bool vis[185];
priority_queue< pair<LL,int> > q;
struct EDGE{
int to,nxt,w;
}edge[3000];
inline void add(int x,int y,int z){
edge[++cnt].nxt=head[x]; edge[cnt].to=y; edge[cnt].w=z; head[x]=cnt;
}
inline int dijkstra(int s,int t){
while(!q.empty()) q.pop();
memset(vis,0,sizeof vis); memset(dis,0x3f,sizeof dis);
q.push(make_pair(0,s)); dis[s]=0;
while(!q.empty()){
int u=q.top().second; q.pop();
if(vis[u]) continue; vis[u]=1;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w){
dis[v]=dis[u]+edge[i].w;
if(!vis[v]) q.push(make_pair(-dis[v],v));
}
}
}
return dis[t];
}
inline void prework(){
for(int i=1;i<(1<<n);i++){
LL p=0,q=1,now=1; syq=0;
for(int j=1;j<=n;j++)
if(i&(1<<(j-1)))
now*=pri[j],sss[++syq]=j;
while(now){
int x=now%10;
p+=x,q*=x; now/=10;
}
p+=5; q+=233;
cnt=0; memset(head,0,sizeof head);
for(int j=1;j<=p;j++){
for(int k=1;k<=syq;k++)
add(j,1LL*j*pri[sss[k]]%p,1);
add(j,(j+1)%p,10);
}
f[i]=dijkstra(q%p,0);
}
}
int main(){
freopen("k.in","r",stdin);
freopen("k.out","w",stdout);
scanf("%lld",&K); LL tmp=K;
for(int i=2;i*i<=K;i++){
if(tmp<i) break;
if(tmp%i==0){
pri[++n]=i;
while(tmp%i==0) tmp/=i;
}
}
if(tmp) pri[++n]=tmp;
prework(); memset(g,0x3f,sizeof g);
for(int i=1;i<(1<<n);i++)
for(int j=i;j;j=i&(j-1)){
g[i]=min(g[i],f[j]+f[i^j]);
}
printf("%lld\n",g[(1<<n)-1]);
return 0;
}
T3 l
有一个长度为
n
n
n 的序列
a
n
a_n
an,初始时
a
i
=
i
a_i = i
ai=i,接下来有
m
m
m 次操作,一次操作表述如下:
• 每次操作有一个参数
b
i
bi
bi。
• 先将当前序列无限复制,变成无限长,后取该序列前 b i bi bi 项作为新的序列。
• 详细解释参见样例。
求
m
m
m 次操作后每个元素在最终序列中出现的次数。
S a m p l e i n p u t Sample\ input Sample input
3 3 9 7 11 3\ 3\ 9\ 7\ 11 3 3 9 7 11
S a m p l e o u t p u t Sample\ output Sample output
5 3 3 5\ 3\ 3 5 3 3
数据范围: n , m ≤ 100000 , b i ≤ 1 0 18 n,m\le 100000,b_i\le 10^{18} n,m≤100000,bi≤1018
solution:
好题好题
一开始是有思路的,就想当前的 b i b_i bi就只和前一个有关,然后就会有一些散块,可以维护整块和散块的个数,最后统计答案。但这么写好像得用一些数据结构,然后就光荣的没有调出来打了个暴力。。
这是我的考场代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100005
#define int long long
#define ls cur<<1
#define rs cur<<1|1
#define len(x) (r[x]-l[x]+1)
using namespace std;
int n,m,b[N],sum[N<<2],l[N<<2],r[N<<2],lazy[N<<2],lazc[N<<2],tot,num=1;
int ans[N];
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
struct qwq{
int pos,lst,cnt;
bool operator <(const qwq &x) const{
return pos<x.pos;
}
}quq[N];
inline void pushup(int cur){
sum[cur]=sum[ls]+sum[rs];
}
inline void pushdown(int cur){
if(lazc[cur]>1){
sum[ls]*=1LL*len(ls)*lazc[cur],lazc[ls]*=lazc[cur];
sum[rs]*=1LL*len(rs)*lazc[cur],lazc[rs]*=lazc[cur];
lazc[cur]=1;
}
if(lazy[cur]){
sum[ls]+=1LL*len(ls)*lazy[cur],lazy[ls]+=lazy[cur];
sum[rs]+=1LL*len(rs)*lazy[cur],lazy[rs]+=lazy[cur];
lazy[cur]=0;
}
}
inline void build(int cur,int L,int R){
if(L==R){
l[cur]=r[cur]=L; lazc[cur]=1;
return;
}
int mid=(L+R)>>1;
build(ls,L,mid); build(rs,mid+1,R);
l[cur]=l[ls],r[cur]=r[rs];
pushup(cur);
}
inline void update_mul(int cur,int L,int R,int c){
if(L<=l[cur] && r[cur]<=R){
sum[cur]*=1LL*len(cur)*c; lazc[cur]*=c;
return;
}
pushdown(cur);
int mid=(l[cur]+r[cur])>>1;
if(L<=mid) update_mul(ls,L,R,c);
if(mid<R) update_mul(rs,L,R,c);
pushup(cur);
}
inline void update_add(int cur,int L,int R,int c){
if(L<=l[cur] && r[cur]<=R){
sum[cur]+=1LL*len(cur)*c; lazy[cur]+=c;
return;
}
pushdown(cur);
int mid=(l[cur]+r[cur])>>1;
if(L<=mid) update_add(ls,L,R,c);
if(mid<R) update_add(rs,L,R,c);
pushup(cur);
}
inline int query(int cur,int pos){
if(l[cur]==pos && r[cur]==pos) return sum[cur];
pushdown(cur);
int mid=(l[cur]+r[cur])>>1;
if(pos<=mid) return query(ls,pos);
else return query(rs,pos);
}
inline int find(int x){
int ll=1,rr=tot,mid,res=tot+1;
while(ll<=rr){
mid=(ll+rr)>>1;
if(quq[mid].pos-quq[mid].lst<=x) rr=mid-1,res=mid;
else ll=mid+1;
} return res;
}
inline void solve1(){
if(m==1) {
int x=rd();
int pos=x%n;
for(int i=1;i<=n;i++)
if(i<=pos) printf("%lld\n",x/n+1);
else printf("%lld\n",x/n);
return;
}
for(int i=1;i<=n;i++) b[i]=i;
int now=n;
while(m--){
int x=rd();
if(x>now){
for(int i=now+1;i<=x;i++)
b[i]=b[i-now];
}
now=x;
}
for(int i=1;i<=now;i++)
ans[b[i]]++;
for(int i=1;i<=n;i++)
printf("%lld\n",ans[i]);
return;
}
signed main(){
freopen("l.in","r",stdin);
freopen("l.out","w",stdout);
n=rd(); m=rd();
if(m==1 || b[1]<=100000) {solve1();return 0;}
int now=n,p; build(1,1,m);
for(int i=1;i<=m;i++) b[i]=rd();
for(int i=m-1;i;i--) b[i]=min(b[i],b[i+1]);
n=min(n,b[1]);
for(int i=1;i<=m;i++){
if(i!=1) now=b[i-1];
p=b[i]%now; num=num*(b[i]/now);
if(b[i]/now && tot>=1) update_mul(1,1,tot,b[i]/now);
if(!p) continue;
int pre=find(p);
if(pre==tot+1){
if((p-quq[pre].pos)%n==0) continue;
num+=quq[pre].cnt;
quq[++tot].lst=(p-quq[pre].pos)%n;
quq[tot].pos=b[i];
quq[tot].cnt=num;
update_add(1,1,tot,1); continue;
}
else if(quq[pre].pos<=p && quq[pre].pos-quq[pre].lst>=p){
num+=quq[pre-1].cnt;
quq[++tot].pos=b[i]; quq[tot].lst=p; quq[tot].cnt=num;
if(pre-1>=1) update_add(1,1,pre-1,1);
}
else{
num+=quq[pre].cnt;
quq[++tot].lst=(b[i]-quq[pre].pos)%n;
quq[tot].pos=b[i];
quq[tot].cnt=num;
if(pre>1) update_add(1,1,pre-1,1);
}
update_add(1,tot,tot,1);
}
ans[1]+=num; ans[n+1]-=num;
for(int i=1;i<=tot;i++){
int sm=query(1,i);
ans[1]+=sm,ans[quq[i].lst+1]-=sm;
}
for(int i=1;i<=n;i++)
ans[i]+=ans[i-1],printf("%lld\n",ans[i]);
return 0;
}
又长又麻烦···其实正解非常短非常简单
就是考虑倒着枚举 b b b,然后可以用当前的 b b b把后面更长的段变小,于是就可以把段存进一个优先队列,每次取比当前枚举长度大的,把它变小,然后更新当前长度的个数,这个个数可以用一个 m a p map map来存,最后到长度比 n n n小的时候就是答案了。
但有这么几点要注意:首先你往队列存的时候不能存重复了,这个可以判断一下 m a p map map的值是否为 0 0 0,其次就是统计答案的时候只有最后在队列里的那些是答案,所以要把他们再存出来。还有最后统计答案可以倒序用差分的思想,这样复杂度就是 O ( n ) O(n) O(n),总复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。
放上 A C AC AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define maxn 100005
#define int long long
using namespace std;
int n,m,b[maxn],ans[maxn],cnt[maxn];
priority_queue<int> q;
map<int,int> mp;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
inline int min(int x,int y){return x<y?x:y;}
signed main(){
freopen("l.in","r",stdin);
freopen("l.out","w",stdout);
n=rd(); m=rd();
for(int i=1;i<=m;i++) b[i]=rd();
for(int i=m-1;i;i--) b[i]=min(b[i],b[i+1]);
b[0]=n; q.push(b[m]); mp[b[m]]++;
for(int i=m-1;i>=0;i--){
while(!q.empty()){
int now=q.top();
if(now<=b[i]) break;
q.pop();
if(mp[b[i]]==0) q.push(b[i]);
mp[b[i]]+=now/b[i]*mp[now];
if(mp[now%b[i]]==0) q.push(now%b[i]);
mp[now%b[i]]+=mp[now];
}
}
while(!q.empty()){
int now=q.top();
if(now<=n) cnt[now]+=mp[now]; q.pop();
}
for(int i=n;i;i--)
ans[i]=cnt[i]+ans[i+1];
for(int i=1;i<=n;i++)
printf("%lld\n",ans[i]);
return 0;
}
今天好水啊连续眼瞎 Q A Q QAQ QAQ,以后要保护好我 5.2 5.2 5.2的眼睛