@
两天的一二题都挺水的,期望本来可以得400,但是没有一天是得完了能得的分的。
DAY1
T1
优美的字符串(string)
【题目描述】
小 Y 送给小 F 一个字符串作为礼物,这个字符串只由’a’ 和’b’ 组成。
由于小 F 患有严重的强迫症,他觉得这个字符串并不优美,他决定对它做一些
操作:
每次操作从字符串中选择一个’ab’ 子串,并将其替换为’bba’。
如果一个字符串的所有’b’ 都在所有’a’ 前面,他认为这个字符串是优美的。
现在小 F 想知道,最少需要多少次操作,能使这个字符串是优美的,或者这个字
符串不可能变成优美的。
【输入格式】
从文件 string.in 中读入数据。
一行一个只由’a’ 和’b’ 组成的字符串。
【输出格式】
输出到文件 string.out 中。
输出一行一个整数,如果无解,输出“-1”。否则输出最少操作次数对.
1000000007
取. 模. 。
大水题,手推五分钟就切了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1000007;
const ll mod=1000000007;
ll dis[N];
char c[N];
template<class T>inline void read(T &res){
static char ch;T flag=1;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
}
int main()
{
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
scanf("%s",c);
int n=strlen(c),tot=0;
ll ans=0;
c[n]='a';
for(int i=n-1,last=n;i>=0;--i)
if(c[i]=='a'){
dis[++tot]=last-i-1;
last=i;
}
for(int i=1;i<=tot;++i){
ans=(ans+dis[i])%mod;
dis[i+1]=(dis[i]*2+dis[i+1])%mod;
}
printf("%lld\n",ans);
return 0;
}
T2
【题目描述】
小 X 同学有很强的计算能力,现在他正在玩一个游戏。
现在有一个正整数 x,每次操作他会将当前的数变为这个数写成二进制后 1 的
个数。
小 X 不断的进行操作,直到这个数变成 1 为止。
由于小 X 的计算能力很强,他现在给出一个 n,他想知道有多少不超过 n 的正整
数会在 k 次操作后变成 1。由于答案可能很大,请对 1000000007 取模。
【输入格式】
从文件 number.in 中读入数据。
第一行一个用二进制表示的正整数 n,含义如题目描述。
第二行一个整数 k, 含义如题目描述。
【输出格式】
输出到文件 number.out 中。
输出一个整数,表示答案对 1000000007 取模的值。
发现2^1000经过一次计算就降到了1000一下,所以可以1000一下暴力搜索,然后再用组合数求值。
记得特判下k=0和1的情况
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=10007;
const ll mod=1000000007;
int n,k,lim,num[N];
ll ans,fac[N]={1};
char ch[N];
ll fp(ll x,ll k){
ll ans=1,s=x;
while(k){
if(k&1) ans=ans*s%mod;
k>>=1;
s=s*s%mod;
}
return ans;
}
inline ll inv(ll x){return fp(x,mod-2);}
inline ll C(ll n,ll m){
if(n>m||n<0||m<0) return 0;
return fac[m]*inv(fac[n])%mod*inv(fac[m-n])%mod;
}
ll cal(int x){
ll res=0,cnt=0;
for(int i=0;i<n;++i)
if(num[i]==1){
res=(res+C(x-cnt,n-i-1))%mod;
++cnt;
}
if(cnt==x) res=(res+1)%mod;
return res;
}
void dfs(int onenum,int stp){
if(stp==k){
ans=(ans+cal(onenum))%mod;
return;
}
if(onenum>lim) return;
for(int i=max(fp(2,onenum)-1,2ll);i<=n;++i){
int cnt=0,temp=i;
while(temp){
if(temp&1) ++cnt;
temp>>=1;
}
if(cnt==onenum) dfs(i,stp+1);
}
}
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
scanf("%s",ch);
n=strlen(ch);
scanf("%d",&k);
if(k==0){
printf("1\n");
return 0;
}
for(int i=0;i<n;++i) num[i]=ch[i]-'0';
lim=log(n)/log(2)+1;
for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mod;
dfs(1,1);
if(k==1) --ans;
printf("%lld\n",ans);
return 0;
}
T3
【题目描述】
S 市的有着非常特殊的城市规划,可以把它抽象成有 n 个点 n 条边的连通图。
S 市的市长对这样的城市规划十分不满意,他想删去其中的一条路使得任意两点间
有且仅有一条简单路径。
S 市的市民对这样的城市规划也十分不满意,具体地,市民的不满意度为任意两点
间最短距离的最大值。
现在小 X 想知道,删去满足条件的一条道路后,市民不满意度的最小值。
【输入格式】
从文件 city.in 中读入数据。
第一行一个正整数 n,表示城市的点数和边数。
接下来 n 行,每行三个正整数 ui
, vi
,wi,表示每条边的两个端点和长度。
【输出格式】
输出到文件 city.out 中。
输出一个整数,表示答案
考虑删掉一条边后,什么样的一条链会成为直径:
把这个图看成是很多棵树由一个环相连:
1.一棵树的直径就是这个图的直径。
2.两个树最深的两个点之间的路径。
删掉环上一条边显然不会影响第一种情况,考虑最小化第二种情况:
假设环的长度为k,环上的点为a1,a2,...,ak,令a0=ak。
令si表示ai到a1的距离。显然这是一个前缀和。
以这个点为根的树上最深的点深度为dep1,dep2,…,depk。
如果断开的路径为e(a{i-1},ai),分为三种情况:
1.从1-i-1选两个点能取到最大值。
2.从i-k选两个点取到最大值。
3.前后各选一个点取到最大值。
对于每条边,我们需要快速的求出三种情况的max。
对于第一种情况,两点x,y间的距离为depx+depy+(sy-sx)=(depx-sx)+(depy+sy)
对于第二种情况结果同第一种情况。
对于第三种情况:设x<y,距离为depx+depy+(sk-(sy-sx))=sk+(depx+sx)+(depy-sy)。
我们需要让三种情况的最大值最小。考虑求每种情况的最大值:
预处理depi-si,depi+si,维护前缀和后缀最大值,就能得到每种情况的最大值,记录所有最大值的最小值,就是答案。
反正我不会
DAY2
T1
【题目描述】
为缓解 S 城与日俱增的交通压力,S 城的市长准备修一条路。
S 城共有 n 个街区,它们由 m 条双向道路相连,每条道路的长度相等。
作为 S 城的天才,小 X 了解到 S 城的交通压力主要来自于最繁华的 S 街区和 T
街区。如果新修的路不能使 S 街区到 T 街区的距离缩短,就不能缓解 S 城的交通压力。
S 城的市长自然不了解这一点。现在小 X 想知道,有多少种修路方案不能缓解 S
城的交通压力。
注意,对于修路方案 (ui
, ti) 和 (ti
, ui) 视为同一种方案,新修的路不能在原图中存在。
【输入格式】
从文件 road.in 中读入数据。
第一行四个整数 n, m, S, T。表示 S 城的街区数,道路数,繁华的两个街区的编号。
接下来 m 行,每行两个数 ui
, vi 表示一条道路上的两个街区。
图中无重边和自环。
【输出格式】
输出到文件 road.out 中。
输出一行一个整数。表示不能缓解交通压力的方案数。
bfs求任意两点间的最短路,然后暴力枚举任意两点连边是否可以使最短路变短(类似dijsktra和floyed的松弛操作)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2007;
int n,m,S,T,tot,ans,head[N],dis[N][N];
bool vis[N],edge[N][N];
struct Edge{
int to,next;
}e[N<<1];
void add(int from,int to){
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
template<class T>inline void read(T &res){
static char ch;T flag=1;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
}
queue<int> q;
void bfs(int sta){
memset(vis,false,sizeof(vis));
q.push(sta);
vis[sta]=true;
while(q.size()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(vis[v]) continue;
vis[v]=true;
dis[sta][v]=dis[sta][u]+1;
q.push(v);
}
}
}
int main()
{
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
read(n);read(m);read(S);read(T);
for(int i=1;i<=m;++i){
int u,v;
read(u);read(v);
add(u,v);add(v,u);
edge[u][v]=edge[v][u]=true;
}
for(int i=1;i<=n;++i) bfs(i);
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j){
if(edge[i][j]) continue;
if(dis[S][T]>dis[S][i]+dis[j][T]+1||dis[S][T]>dis[S][j]+dis[i][T]+1){
++ans;
// printf("%d %d\n",i,j);
}
}
printf("%d\n",n*(n-1)/2-m-ans);
return 0;
}
T2
【题目描述】
everlasting 有 n 个神奇的集合,编号为 1n。开始时它们都是空的,现在 everlasting
要对它们进行两种操作:
- 将元素 x 加入编号 [l,r] 的集合中。神奇的是,如果一个集合原本就有 x,那么
该集合中所有元素的个数都会翻倍 - 询问编号 [l,r] 的集合元素个数的和,对 998244353 取模。
everlasting 当然不会做啦,但是他想考考你...
【输入格式】
从文件 multiset.in 中读入数据。
第一行两个正整数 n, q, 表示集合个数和询问数量。
接下来 q 行,首先是一个整数 opt:
若 opt = 1,接下来三个整数 l,r, x,表示向编号 [l,r] 的集合中加入 x。
若 opt = 2,接下来两个整数 l,r,表示询问编号 [l,r] 的集合的元素个数和。
【输出格式】
输出到文件 multiset.out 中。
对于每个询问,输出一行一个整数,表示答案。
显然可以开n个线段树来维护n个元素的覆盖情况,动态开点防止空间爆炸。剩下就很简单了。
考场调了2.5h也没打出来,旁边的myg大佬一下就打完了,再此膜拜。
#include<cstdio>
using namespace std;
#define ll long long
const ll mod=998244353;
const int N=8000007;
int n,q,ndnum,root,a[N>>5],ls[N],rs[N];
ll sum[N],fla[N],flm[N];
template<class T>inline void read(T &res){
static char ch;T flag=1;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
}
int build(int l,int r){
int p=++ndnum;
if(l==r){
sum[p]=0;
flm[p]=1;
return p;
}
int mid=(l+r)>>1;
ls[p]=build(l,mid);
rs[p]=build(mid+1,r);
sum[p]=sum[ls[p]]+sum[rs[p]];
flm[p]=1;
return p;
}
void pushdown(int p,int l,int r){
int mid=(l+r)>>1;
sum[ls[p]]=(sum[ls[p]]*flm[p]%mod+(mid-l+1)*fla[p]%mod)%mod;
sum[rs[p]]=(sum[rs[p]]*flm[p]%mod+(r-mid)*fla[p]%mod)%mod;
fla[ls[p]]=(fla[ls[p]]*flm[p]%mod+fla[p])%mod;
fla[rs[p]]=(fla[rs[p]]*flm[p]%mod+fla[p])%mod;
flm[ls[p]]=flm[ls[p]]*flm[p]%mod;
flm[rs[p]]=flm[rs[p]]*flm[p]%mod;
fla[p]=0;flm[p]=1;
}
int xl,xr,yl,yr,x;
ll query(int l,int r,int p){
if(xl<=l&&r<=xr) return sum[p];
pushdown(p,l,r);
int mid=(l+r)>>1;
ll res=0;
if(xl<=mid) res+=query(l,mid,ls[p]);
if(xr>mid) res+=query(mid+1,r,rs[p]);
return res%mod;
}
int sett(int l,int r,int p){
if(p==0) p=++ndnum;
if(yl<=l&&r<=yr){
sum[p]=1;
fla[p]=true;
return p;
}
int mid=(l+r)>>1;
if(yl<=mid){
if(!ls[p]) ls[p]=++ndnum;
ls[p]=sett(l,mid,ls[p]);
}
if(yr>mid){
if(!rs[p]) rs[p]=++ndnum;
rs[p]=sett(mid+1,r,rs[p]);
}
if(sum[ls[p]]==sum[rs[p]]) sum[p]=sum[ls[p]];
else sum[p]=-1;
return p;
}
int _get(int l,int r,int p){
if(yl<=l&&r<=yr) return sum[p];
int mid=(l+r)>>1;
int la=100,ra=100;
if(yl<=mid){
if(!ls[p]) ls[p]=++ndnum;
if(fla[p]==1) sum[ls[p]]=fla[ls[p]]=1;
la=_get(l,mid,ls[p]);
}
if(yr>mid){
if(!rs[p]) rs[p]=++ndnum;
if(fla[p]==1) sum[rs[p]]=fla[rs[p]]=1;
ra=_get(mid+1,r,rs[p]);
}
if(la==100) return ra;
if(ra==100) return la;
if(la==ra) return la;
return -1;
}
void modify(int l,int r,int p,int c){
if(xl<=l&&r<=xr){
if(c==1){
sum[p]=(sum[p]+r-l+1)%mod;
fla[p]=(fla[p]+1)%mod;
return;
}
if(c==2){
sum[p]=sum[p]*2%mod;
fla[p]=fla[p]*2%mod;
flm[p]=flm[p]*2%mod;
return;
}
yl=l,yr=r;
int y=_get(1,n,x);
if(y==0) c=1;
else if(y==1) c=2;
else c=0;
if(c==1){
sum[p]=(sum[p]+r-l+1)%mod;
fla[p]=(fla[p]+1)%mod;
return;
}
if(c==2){
sum[p]=sum[p]*2%mod;
fla[p]=fla[p]*2%mod;
flm[p]=flm[p]*2%mod;
return;
}
}
pushdown(p,l,r);
int mid=(l+r)>>1;
if(xl<=mid) modify(l,mid,ls[p],c);
if(xr>mid) modify(mid+1,r,rs[p],c);
sum[p]=(sum[ls[p]]+sum[rs[p]])%mod;
}
int main()
{
freopen("multiset.in","r",stdin);
freopen("multiset.out","w",stdout);
read(n);read(q);
ndnum=n;
root=build(1,n);
while(q--){
int opt;
read(opt);
if(opt==1){
read(xl);read(xr);read(x);
modify(1,n,root,0);
yl=xl;yr=xr;
sett(1,n,x);
}
else if(opt==2){
read(xl);read(xr);
printf("%lld\n",query(1,n,root));
}
}
// while(q--){
// int opt;
// read(opt);
// if(opt==1){
// read(yl);read(yr);read(x);
// sett(1,n,x);
// }
// else if(opt==2){
// read(yl);read(yr);read(x);
// printf("%d\n",_get(1,n,x));
// }
// }
return 0;
}
T3
【题目描述】
小 X 同学觉得树上问题太毒瘤了,于是决定将树上的边删去,最终变成一个点。
现在有一个 n 个节点的树,他的游戏是这样的:
- 从剩下的所有边中等概率随机选中一条边 T。
- 将这条边删去,若这条边相连的两个点编号为 u 和 v,新建一个点 x,这个点与
所有与 u 和 v 相邻的点有边,最后删去 u 和 v 及与它们相连的边,x 的编号等概率随
机命名为 u 或 v。
不断重复上述步骤,知道只剩下一个点。
现在小 X 想知道,对于每个编号,最后剩下该编号的点的概率。
树是一个没有环的连通图。
【输入格式】
从文件 tree.in 中读入数据。
第一行一个整数 n。
接下来 n 1 行,第 i 行两个整数 ui
, vi,表示第 i 条边连接的两个点。