2018.10.31模拟赛

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 WVK,之后付出 f ( V ) f(V ) f(V) 的代价,并将 W W W 变成 W ⋅ V W ·V WV 。当 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 xS 并付出 1 1 1 的代价使 q q q 变成 q ⋅ x q·x qx,直到 p ∣ q p|q pq,完成这个过程所需的最小代价 就是 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,m100000,bi1018

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的眼睛

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值