2023.8.11测试
为什么我天天挂分?????????
T1 数对
简单容斥+莫反,但是最后10min才发现容斥写错了!!!!!(幸好贺了别人的代码通过了)
设
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1000010;
int L,R;
int v[N],prime[N],tot,mu[N];
LL ans,sum[N];
void primes()
{
mu[1]=1;
for(int i=2; i<=1e6; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1; j<=tot && prime[j]<=v[i] && prime[j]<=1e6/i; j++)
{
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1; i<=1e6; i++)
sum[i]=sum[i-1]+(LL)mu[i];
}
LL calc(int n,int m)
{
LL res=1LL*n*m;
for(int l=1,r; l<=n; l=r+1)
{
r=min(n/(n/l),m/(m/l));
res-=1LL*(n/l)*(m/l)*(sum[r]-sum[l-1]);
}
for(int i=2; i<=n; i++)
res-=1LL*((n/i)+(m/i)-1);
return res;
}
int main()
{
primes();
scanf("%d%d",&L,&R);
ans=calc(R,R)-2*calc(L-1,R)+calc(L-1,L-1);
printf("%lld\n",ans);
return 0;
}
T2 小偷与警察
很好的点双练习题,逼我赶紧去恶补圆方树,需要赶紧修订图的连通性相关(Tarjan算法)
先跑一遍 yes
对于割边和割点,边双缩点和点双缩点都要做一遍???
不用!我们惊奇地发现一条割边的两个端点一定是割点,所以可以直接将边的询问转化为点的询问
那现在问题变成了如何确定一个点是否可以拦截罪犯。
对原图进行点双缩点,建出圆方树,那么对于
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
const int N=100010,M=500010,NN=200010;
int n,m,q;
int head[N],ver[2*M],nxt[2*M],tot;
int dfn[N],low[N],num,cut[N],c[N],cnt,sta[N],topp,flag;
int fa[NN],siz[NN],dep[NN],son[NN],top[NN],dft[NN];
vector <int> g[NN];
map < pair<int,int>,int > bridge;
pair<int,int> minmax(int x,int y)
{
return mp(min(x,y),max(x,y));
}
void add(int x,int y)
{
ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot;
ver[++tot]=x; nxt[tot]=head[y]; head[y]=tot;
}
void add_c(int x,int y)
{
g[x].push_back(y);
g[y].push_back(x);
}
void tarjan(int x,int fa)
{
dfn[x]=low[x]=++num;
sta[++topp]=x;
for(int i=head[x]; i; i=nxt[i])
{
int y=ver[i];
if(!dfn[y])
{
tarjan(y,x);
low[x]=min(low[x],low[y]);
if(dfn[x]<=low[y])
{
flag++;
if(flag>1 || x!=1)
cut[x]=1;
cnt++; add_c(cnt,x);
if(dfn[x]<low[y])
bridge[minmax(x,y)]=cnt;
int z;
do
{
z=sta[topp--];
add_c(cnt,z);
}while(y!=z);
}
}
else if(y!=fa)
low[x]=min(low[x],dfn[y]);
}
}
void dfs1(int x,int f)
{
son[x]=-1; siz[x]=1;
dep[x]=dep[fa[x]=f]+1;
for(int i=0; i<g[x].size(); i++)
{
int y=g[x][i];
if(y==f)
continue;
dfs1(y,x);
if(son[x]==-1 || siz[y]>siz[son[x]])
son[x]=y;
siz[x]+=siz[y];
}
}
void dfs2(int x,int t)
{
top[x]=t; dft[x]=++dft[0];
if(son[x]==-1)
return;
dfs2(son[x],t);
for(int i=0; i<g[x].size(); i++)
{
int y=g[x][i];
if(y==fa[x] || y==son[x])
continue;
dfs2(y,y);
}
}
bool check(int x,int y,int z)
{
bool pd=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
swap(x,y);
if(top[x]==top[z] && dep[x]>=dep[z])
pd=1;
x=fa[top[x]];
}
if(pd)
return 1;
if(dep[x]<dep[y])
swap(x,y);
if(top[x]==top[z] && dep[x]>=dep[z] && dep[y]<=dep[z])
return 1;
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
cnt=n;
tarjan(1,0);
dfs1(1,0);
dfs2(1,1);
scanf("%d",&q);
while(q--)
{
int op,a,b,c,x,y;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d%d",&a,&b,&x,&y);
map< pair<int,int>,int>::iterator it=bridge.find(minmax(x,y));
if(it==bridge.end())
printf("yes\n");
else if(check(a,b,it->second))
printf("no\n");
else
printf("yes\n");
}
else
{
scanf("%d%d%d",&a,&b,&x);
if(!cut[x])
printf("yes\n");
else if(check(a,b,x))
printf("no\n");
else
printf("yes\n");
}
}
return 0;
}
T3 无线网络
无限接近正解,但还是输了……
二分答案最后的负担值
其实字典序处理很简单,在我们二分知道最后的负担值
#include<bits/stdc++.h>
using namespace std;
const int N=60,M=3010,INF=1e9;
int n,m,s,t,maxflow,id,d[2*N],fa[N];
int head[2*N],ver[2*M],nxt[2*M],edge[2*M],now[2*N],tot=1;
char str[N][N];
void add(int x,int y,int z)
{
ver[++tot]=y; edge[tot]=z; nxt[tot]=head[x]; head[x]=tot;
ver[++tot]=x; edge[tot]=0; nxt[tot]=head[y]; head[y]=tot;
}
bool bfs()
{
memset(d,0,sizeof(d));
queue <int> q;
q.push(s); d[s]=1;
now[s]=head[s];
while(q.size())
{
int x=q.front(); q.pop();
for(int i=head[x]; i; i=nxt[i])
{
if(edge[i] && !d[ver[i]])
{
int y=ver[i];
q.push(y);
now[y]=head[y];
d[y]=d[x]+1;
if(y==t)
return 1;
}
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x==t)
return flow;
int rest=flow,k;
for(int &i=now[x]; i && rest; i=nxt[i])
{
if(edge[i] && d[ver[i]]==d[x]+1)
{
int y=ver[i];
k=dinic(y,min(rest,edge[i]));
if(!k)
d[y]=0;
edge[i]-=k;
edge[i^1]+=k;
rest-=k;
if(rest<=0)
break;
}
}
return flow-rest;
}
void Dinic()
{
int flow=0;
while(bfs())
while(flow=(dinic(s,INF)))
maxflow+=flow;
return;
}
bool check(int k)
{
tot=1;
memset(head,0,sizeof(head));
for(int i=1; i<=n; i++)
{
add(s,i,1);
if(fa[i])
add(i,fa[i]+n,1);
else if(str[i][n+1]=='Y')
add(i,t,1);
else
for(int j=1; j<=n; j++)
if(str[i][j]=='Y')
add(i,j+n,1);
add(i+n,t,k);
}
maxflow=0;
Dinic();
if(maxflow==n)
return 1;
return 0;
}
int find()
{
int lo=-1,hi=n;
while(lo+1<hi)
{
int mid=(lo+hi)>>1;
if(check(mid))
hi=mid;
else
lo=mid;
}
return hi;
}
int main()
{
scanf("%d",&n);
for(int i=0; i<=n; i++)
scanf("%s",str[i]+1);
for(int i=1; i<=n; i++)
str[i][n+1]=str[0][i];
s=0; t=2*n+1;
int ans=find();
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n+1; j++)
{
if(str[i][j]=='Y')
{
fa[i]=j;
if(check(ans))
break;
}
}
}
for(int i=1; i<=n; i++)
printf("%d ",fa[i]-1);
return 0;
}
T4 游戏设计
对于所有长度为
对于一个字符串
-
交换相邻的两个字母
-
如果字符串中含有子串
,可以将它替换成子串 (保证 )
如果能从某个字符串
多组测试数据,
赛时没开,其实不是很难
因为可以交换相邻的字符,所以每种字母出现个数相同的字符串可以通过操作
容易发现,有些状态可以通过操作
#include<bits/stdc++.h>
#define LL long long
#define mp make_pair
using namespace std;
const int N=6010,NN=40,M=60,MM=36000010;
int T,k,m,n,cnt[55][4][2],deg[N],tid;
int dfn[N],low[N],ins[N],c[N],sta[N],top,tc;
int head[N],ver[MM],nxt[MM],from[MM],tot;
LL C[NN][NN],val[N],a[N],f[N],ans;
char str1[M][NN],str2[M][NN];
vector <int> scc[N],g[N];
map <pair< pair<int,int>,pair<int,int> >,int> fid;
void init()
{
tot=top=ans=tid=0;
memset(head,0,sizeof(head));
memset(f,0,sizeof(f));
memset(cnt,0,sizeof(cnt));
memset(deg,0,sizeof(deg));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(c,0,sizeof(c));
fid.clear();
for(int i=1; i<=tc; i++)
scc[i].clear(),g[i].clear();
tc=0;
}
void prework()
{
for(int i=0; i<=35; i++)
C[i][0]=1;
for(int i=1; i<=35; i++)
for(int j=1; j<=i; j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
}
void add(int x,int y)
{
ver[++tot]=y; from[tot]=x;
nxt[tot]=head[x]; head[x]=tot;
}
void add_c(int x,int y)
{
g[x].push_back(y); deg[y]++;
}
void tarjan(int x)
{
dfn[x]=low[x]=++dfn[0];
sta[++top]=x; ins[x]=1;
for(int i=head[x]; i; i=nxt[i])
{
int y=ver[i];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(ins[y])
low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
tc++; int y;
do
{
y=sta[top--]; ins[y]=0;
c[y]=tc; scc[tc].push_back(y);
}while(x!=y);
}
}
LL count(int a,int b,int c,int d)
{
return C[a+b+c+d][a]*C[b+c+d][b]*C[c+d][c];
}
void topsort()
{
queue <int> q;
for(int i=1; i<=tc; i++)
if(!deg[i])
f[i]=a[i],q.push(i);
while(q.size())
{
int x=q.front(); q.pop();
int len=g[x].size();
for(int i=0; i<len; i++)
{
int y=g[x][i];
f[y]=max(f[y],f[x]+a[y]);
deg[y]--;
if(!deg[y])
q.push(y);
}
}
for(int i=1; i<=tc; i++)
ans=max(ans,f[i]);
}
int main()
{
prework();
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&k,&m);
for(int a=0; a<=k; a++)
{
for(int b=0; a+b<=k; b++)
{
for(int c=0; a+b+c<=k; c++)
{
int d=k-a-b-c;
fid[mp(mp(a,b),mp(c,d))]=++tid;
val[tid]=count(a,b,c,d);
}
}
}
for(int i=1; i<=m; i++)
{
scanf("%s",str1[i]+1);
int len=strlen(str1[i]+1);
for(int j=1; j<=len; j++)
cnt[i][str1[i][j]-'A'][0]++;
}
for(int i=1; i<=m; i++)
{
scanf("%s",str2[i]+1);
int len=strlen(str2[i]+1);
for(int j=1; j<=len; j++)
cnt[i][str2[i][j]-'A'][1]++;
int aa=max(cnt[i][0][0],cnt[i][0][0]-cnt[i][0][1]);
int bb=max(cnt[i][1][0],cnt[i][1][0]-cnt[i][1][1]);
int cc=max(cnt[i][2][0],cnt[i][2][0]-cnt[i][2][1]);
int dd=max(cnt[i][3][0],cnt[i][3][0]-cnt[i][3][1]);
for(int a=aa; a+bb+cc+dd<=k; a++)
{
for(int b=bb; a+b+cc+dd<=k; b++)
{
for(int c=cc; a+b+c+dd<=k; c++)
{
int d=k-a-b-c;
int id=fid[mp(mp(a,b),mp(c,d))];
int aaa=a-(cnt[i][0][0]-cnt[i][0][1]);
int bbb=b-(cnt[i][1][0]-cnt[i][1][1]);
int ccc=c-(cnt[i][2][0]-cnt[i][2][1]);
int ddd=d-(cnt[i][3][0]-cnt[i][3][1]);
int idd=fid[mp(mp(aaa,bbb),mp(ccc,ddd))];
if(id==idd)
continue;
add(id,idd);
}
}
}
}
n=C[k+4-1][4-1];
for(int i=1; i<=n; i++)
if(!dfn[i])
tarjan(i);
for(int i=1; i<=tot; i++)
{
int x=from[i],y=ver[i];
if(c[x]==c[y])
continue;
add_c(c[x],c[y]);
}
for(int i=1; i<=tc; i++)
{
int len=scc[i].size(); a[i]=0;
for(int j=0; j<len; j++)
a[i]+=val[scc[i][j]];
}
topsort();
printf("%lld\n",ans);
}
return 0;
}
总结
-
T1 没测大样例就直接跑路了,以后还是需要谨慎些,对于这种数学题要多测几组大样例
-
T2 写错解写了太久了,以后写代码前应试试自己做法是否正确,拿样例试一下,不能直接开写。对于圆方树的知识点,不会没办法
-
T3 对于字典序的处理有失偏颇,应该要注意到数据范围很小可以暴力枚举的。数组开小这种问题不能再犯
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现