T1 lgg
L 君和 G 君在玩一个游戏。G 君写下一个字符串 A,L 君将其复制一遍连接到 A 串后面得
到字符串 B, G 君又在 B 的任意位置(包括首尾)插入一个字符得到字符串 C。现在你得到了 C,
请你找出 A。
第一行一个整数 TTT,表示有 TTT 组数据
每组数据第一行一个整数 nnn,表示字符串 CCC 的长度。
每组数据第二行 nnn 个大写字母,表示字符串 CCC。
若不存在可能的字符串 AAA,输出一行“NOT POSSIBLE”“NOT\ POSSIBLE”“NOT POSSIBLE” ;若存在多个不同的字符串 AAA 满
足条件,输出一行“NOT UNIQUE”“NOT\ UNIQUE”“NOT UNIQUE” ;否则输出一行为满足条件的字符串 AAA。
solution:
裸的哈希,然后用mapmapmap判一下重就好了,考场眼瞎没看见不同的字符串,直接000分qnmdqnmdqnmd
放ACACAC代码
#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
小 YYY 最近加冕为西班牙皇帝了! 小 YYY 想跟你分享他多年奋斗的人生经验,虽然你表示完全不想理他,但还是被拖了过去。
具体来说,小 YYY 有一个威望值的属性,初始为 111,为了成为皇帝,他需要达到 KKK 的威 望值。获得威望值的方法如下:假设小 YYY 现在的威望值为 WWW,那么小 YYY 每次会宣称一个 数 VVV ,满足 W⋅V∣KW ·V|KW⋅V∣K,之后付出 f(V)f(V )f(V) 的代价,并将 WWW 变成 W⋅VW ·VW⋅V 。当 W=KW = KW=K 时,小 YYY 就胜利了。 f(V)f(V )f(V) 是攻下 VVV 的代价,定义 ppp 为 VVV 的十进制各位数字之和加上 555,qqq 为 VVV 的十进制 各位数字之积加上 233233233,SSS 为 VVV 的质因子集合。每次可以付出 101010 的代价使 qqq 变成 q+1q + 1q+1, 或者选定 x∈Sx ∈ Sx∈S 并付出 111 的代价使 qqq 变成 q⋅xq·xq⋅x,直到 p∣qp|qp∣q,完成这个过程所需的最小代价 就是 f(V)f(V )f(V)。 这实在是太复杂了,你深刻地体会到皇帝不好当,现在你只想知道成为皇帝的最小代 价是多少。 特别地,本题中 ∣µ(K)∣=1|µ(K)| = 1∣µ(K)∣=1,且为了方便,KKK 的最大质因子 ≤106≤ 10^6≤106。
solution:
考试的时候眼瞎看错样例
因为∣µ(K)∣=1|µ(K)| = 1∣µ(K)∣=1,所以它一定是许多质数的乘积,并且没有平方因子,因为这个性质,可以发现KKK最多有151515个质因子,所以就可以状压,考虑枚举子集,然后分成两部分那样算,但枚举子集是3n3^n3n,所以要O(1)O(1)O(1)转移,可以先2n×n2^n\times n2n×n预处理,算出当VVV是某个质数集合的时候的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
有一个长度为 nnn 的序列 ana_nan,初始时 ai=ia_i = iai=i,接下来有 mmm 次操作,一次操作表述如下:
• 每次操作有一个参数 bibibi。
• 先将当前序列无限复制,变成无限长,后取该序列前 bibibi 项作为新的序列。
• 详细解释参见样例。
求 mmm 次操作后每个元素在最终序列中出现的次数。
Sample inputSample\ inputSample input
3 3 9 7 113\ 3\ 9\ 7\ 113 3 9 7 11
Sample outputSample\ outputSample output
5 3 35\ 3\ 35 3 3
数据范围:n,m≤100000,bi≤1018n,m\le 100000,b_i\le 10^{18}n,m≤100000,bi≤1018
solution:
好题好题
一开始是有思路的,就想当前的bib_ibi就只和前一个有关,然后就会有一些散块,可以维护整块和散块的个数,最后统计答案。但这么写好像得用一些数据结构,然后就光荣的没有调出来打了个暴力。。
这是我的考场代码:
#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;
}
又长又麻烦···其实正解非常短非常简单
就是考虑倒着枚举bbb,然后可以用当前的bbb把后面更长的段变小,于是就可以把段存进一个优先队列,每次取比当前枚举长度大的,把它变小,然后更新当前长度的个数,这个个数可以用一个mapmapmap来存,最后到长度比nnn小的时候就是答案了。
但有这么几点要注意:首先你往队列存的时候不能存重复了,这个可以判断一下mapmapmap的值是否为000,其次就是统计答案的时候只有最后在队列里的那些是答案,所以要把他们再存出来。还有最后统计答案可以倒序用差分的思想,这样复杂度就是O(n)O(n)O(n),总复杂度O(nlogn)O(nlogn)O(nlogn)。
放上ACACAC代码:
#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;
}
今天好水啊连续眼瞎QAQQAQQAQ,以后要保护好我5.25.25.2的眼睛