《随机化专题》小结
相当于有
显然如果直接正着先取,然后再取负的做不了。
我们考虑如果能不连续一段取正或者连续一段取负,那么我们实际上第二维的值域不需要维护那么大。
这时候引入随机游走这么一个概念,假如你在一个
于是我们将
于是我们做背包就好了。
时间复杂度
点击查看代码
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL inf=-1e18;
const int MAXN=1000*40*10;
int T,cnt,n,m,pp;
struct ddl {
int a,b;
}a[MAXN];
LL f[MAXN],g[MAXN];
int main () {
srand(time(0));
scanf("%d",&T);
while(T--) {
cnt=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) {
int u,v;
scanf("%d%d",&u,&v);
a[++cnt].a=u; a[cnt].b=v;
}
for(int i=1;i<=m;++i) {
int u,v;
scanf("%d%d",&u,&v);
a[++cnt].a=-u; a[cnt].b=v;
}
int RR=sqrt(n)*1000;
int p=RR; RR*=2;
random_shuffle(a+1,a+1+cnt);
for(int j=0;j<=RR;++j) f[j]=inf;
f[p]=0;
for(int j=1;j<=cnt;++j) {
for(int q=0;q<=RR;++q) {
g[q]=f[q];
}
for(int q=0;q<=RR;++q) {
if(q-a[j].a>=0&&q-a[j].a<=RR) {
f[q]=max(f[q],g[q-a[j].a]+a[j].b);
}
}
}
printf("%lld\n",f[p]);
}
return 0;
}
显然题目保证有解,也就是说至少
每次我们随便选三个点出来,正确的概率是
不过对于求三个点的圆心,是数学的东西,我不想管,直接抄代码。
点击查看代码
#include<bits/stdc++.h>
#define pi pair<DB,DB>
#define fi first
#define se second
#define eps 1e-9
typedef long long LL;
typedef double DB;
using namespace std;
const int MAXN=1e5+10;
double x,y,r;
int T;
int n;
struct ddl {
DB x,y;
}a[MAXN];
DB sq(DB x) {
return x*x;
}
double dis(int p1,int p2) {
return sqrt(sq(a[p1].x-a[p2].x)+sq(a[p1].y-a[p2].y));
}
double dis2(int E) {
return sqrt(sq(a[E].x-x)+sq(a[E].y-y));
}
void cal(int E,int b,int c) {
double a1=a[b].x-a[E].x, b1=a[b].y-a[E].y, c1=(a1*a1+b1*b1)/2;
double a2=a[c].x-a[E].x, b2=a[c].y-a[E].y, c2=(a2*a2+b2*b2)/2;
double d=a1*b2-a2*b1;
x=a[E].x+(c1*b2-c2*b1)/d, y=a[E].y+(a1*c2-a2*c1)/d;
r=dis2(E);
}
DB gt(DB x,DB y,DB xx,DB yy) {
return sqrt(sq(x-xx)+sq(y-yy));
}
bool check(int p1,int p2,int p3) {
cal(p1,p2,p3);
int cnt=0;
for(int i=1;i<=n;++i) {
if(fabs(dis2(i)-r)<eps) ++cnt;
}
if(cnt*2>=n) {
printf("%.6lf %.6lf %.6lf\n",x,y,r);
return true;
}
return false;
}
int main () {
srand(114514);
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
for(int i=1;i<=n;++i) {
scanf("%lf%lf",&a[i].x,&a[i].y);
}
if(n<=2) {
printf("%.6lf %.6lf %.6lf\n",a[1].x,a[1].y-1,1.0);
continue;
}
if(n<=4) {
double x=a[1].x+a[2].x,y=a[1].y+a[2].y;
double R=dis(1,2);
printf("%.6lf %.6lf %.6lf\n",x/2,y/2,R/2);
continue;
}
while(1) {
int p1=rand()%n+1,p2=rand()%n+1,p3=rand()%n+1;
if(p1==p2||p2==p3||p1==p3) continue;
if(check(p1,p2,p3)) break;
}
}
return 0;
}
P7146 [THUPC2021 初赛] 独立
首先直接做的话显然是完全做不了的。然后这个东西一看就要
但是如果你直接做有做不了,怎么办。
这时候我们想起来数据是随机生成的,手玩一下,可以发现,由于边很少,所以环的个数和大小都很少。
至多只有
对于没有环的
对于每条环上的边,暴力枚举他会不会两边的点都选,如果没有,就再枚举一下他两边哪个点不选。
上面这个东西要枚举子集,时间复杂度
然后每次要做一次
由于
点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long LL;
using namespace std;
const LL inf=1e15;
const LL MAXN=2e5+10;
LL n,m,x,y,z,q=101,b=137,p=1e9+7;
LL a[MAXN];
map<LL,LL> mp[MAXN];
struct ddl {
LL f,t,c;
}que[MAXN*2];
int cnt=1,h[MAXN];
void add(int f,int t,int c) {
que[++cnt].f=h[f];
que[cnt].t=t;
que[cnt].c=c;
h[f]=cnt;
}
LL xx,yy,zz;
void init() {
scanf("%lld%lld%lld%lld%lld%lld",&n,&m,&xx,&yy,&a[0],&zz);
for(int i=1;i<=n;++i) {
a[i]=(q*a[i-1]+b)%p;
}
for(int i=1;i<=m;++i) {
xx=(q*xx+b)%p;
yy=(q*yy+b)%p;
zz=(q*zz+b)%p;
int x=xx%n+1,y=yy%n+1;
if(!mp[x].count(y)&&x!=y) {
mp[x][y]=1;
mp[y][x]=1;
add(x,y,zz);
add(y,x,zz);
}
}
}
int dfn[MAXN],rt[MAXN];
bool g[MAXN];
struct circle {
LL f,t,c;
}E[MAXN];
int tot,ban[MAXN*2],st[MAXN],top,zt[MAXN];
void dfs(int u,int fa) {
dfn[u]=++cnt;
if(u==fa) rt[u]=1;
for(int i=h[u];i;i=que[i].f) {
int t=que[i].t;
if(t==fa) continue;
if(!dfn[t]) {
dfs(t,u);
g[u]|=g[t];
}
else {
if(dfn[t]<dfn[u]) {
E[++tot]={t,u,que[i].c},ban[i]=1,ban[(i^1)]=1,g[u]=1;
}
}
}
}
LL dp[MAXN][2],root[MAXN],fgo,H[MAXN],qls;
bool zk[MAXN];
void DFS(int u,int fa) {
zk[u]=1;
for(int i=h[u];i;i=que[i].f) {
int t=que[i].t;
if(t==fa||ban[i]) continue;
DFS(t,u);
dp[u][0]+=max(dp[t][0],dp[t][1]);
dp[u][1]+=max(dp[t][1]-que[i].c,dp[t][0]);
}
}
void work() {
cnt=0;
for(int i=1;i<=n;++i) {
if(!dfn[i]) {
dfs(i,i);
}
}
for(int i=1;i<=tot;++i) {
st[++top]=E[i].f;
st[++top]=E[i].t;
}
sort(st+1,st+1+top);
top=unique(st+1,st+1+top)-st-1;//环上的点
int limit=(1<<top)-1;
LL ans=0;
for(int i=1;i<=n;++i) dp[i][0]=0,dp[i][1]=a[i];
for(int i=1;i<=n;++i) {
if(rt[i]&&!(g[i])) {
DFS(i,i);
ans+=max(dp[i][0],dp[i][1]);
}
}
for(int i=1;i<=n;++i) {
if(!zk[i]) {
H[++qls]=i;
}
}
for(int i=1;i<=n;++i) {
if(!zk[i]&&rt[i]) {
root[++fgo]=i;//环上点的根
}
}
limit=(1<<tot)-1;
LL da=ans;
for(int i=0;i<=limit;++i) {//强制某些环边贡献
LL ls_ans=ans;
for(int j=1;j<=top;++j) zt[st[j]]=0;
for(int j=1;j<=tot;++j) {
if((i&(1<<(j-1)))) {
ls_ans-=E[j].c;//枚举被造成贡献的根
zt[E[j].f]=zt[E[j].t]=1;//是否被强制选择
}
}
int lim=(i^limit);
for(int j=lim;;j=((j-1)&lim)) {//枚举没被选择的环边
for(int q=1;q<=qls;++q)
dp[H[q]][1]=a[H[q]],dp[H[q]][0]=0;
for(int q=1;q<=qls;++q) if(zt[H[q]])
dp[H[q]][0]=-inf;
for(int q=1;q<=tot;++q) {
if(!((1<<(q-1))&lim)) continue;
if((1<<(q-1))&j) {
dp[E[q].t][1]=-inf;
}
else {
dp[E[q].f][1]=-inf;
}
}
LL ls_a=ls_ans;
for(int q=1;q<=fgo;++q) {
DFS(root[q],root[q]);
ls_a+=max(dp[root[q]][0],dp[root[q]][1]);
}
da=max(da,ls_a);
if(!j) break;
}
}
printf("%lld\n",da);
}
signed main() {
init();
work();
return 0;
}
棋盘上的旅行
对于至少走
考虑每次给每种颜色再随意染一种颜色,值域为
然后我们每次正确的概率是
不过我们多跑几次,跑个
然后就可以通过了,脸黑可以多跑几次。
启示:
对于这题如果直接做是做不了的,因为你要记得信息太多了,于是我们考虑加强题目限制,将一部分颜色变成同一种颜色,这样我们在保证一部分正确率的情况下不用记录太多信息,只需要较少的信息量即可完成题目。
对于一些题目,如果原问题限制太弱,要太多信息,可以加强限制,减少信息的需要量。
点击查看代码
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN=30;
int T;
int n,m,k;
int a[MAXN][MAXN];
int f[MAXN][MAXN][(1<<7)];
int col[MAXN*MAXN],ans;
int dx[10+10]={0,0,1,-1};
int dy[10+10]={1,-1,0,0};
struct dl {
int x,y,j;
};
queue<dl> q;
void zx() {
// cout<<114514<<' ';
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j) {
for(int q=0;q<(1<<k);++q) {
f[i][j][q]=-1;
}
}
}
for(int i=1;i<=n*m;++i) col[i]=rand()%k+1;
queue<dl> q;
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j) {
if(a[i][j]==-1) continue;
int t=(1<<(col[a[i][j]]-1));
if(a[i][j]==0) t=0;
f[i][j][t]=0;
q.push({i,j,t});
}
}
while(!q.empty()) {
dl ls=q.front();
q.pop();
for(int i=0;i<4;++i) {
int xx=ls.x+dx[i];
int yy=ls.y+dy[i];
int t=ls.j;
if(a[xx][yy]==-1) continue;
if(col[a[xx][yy]]>0) t|=(1<<(col[a[xx][yy]]-1));
if(xx<1||yy<1||xx>n||yy>m||f[xx][yy][t]>=0) continue;
f[xx][yy][t]=f[ls.x][ls.y][ls.j]+1;
q.push({xx,yy,t});
}
}
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j) {
if(f[i][j][(1<<k)-1]==-1) continue;
ans=min(ans,f[i][j][(1<<k)-1]);
}
}
}
int main() {
srand(19260817);
scanf("%d",&T);
while(T--) {
ans=114514;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j) {
scanf("%d",&a[i][j]);
}
}
double sta=clock();
for(int i=1;i<=450;++i) {
zx();
}
if(ans==114514) {
puts("-1");
}
else {
printf("%d\n",ans);
}
}
return 0;
}
模范学长
我不会行列式,看这个题解,想看的看这个题解,看我看我
度度熊算算数
首先考虑为一条链的情况。
直接做可以获得一个
但是由于是环,考虑破环成链,枚举开始点。
这样我们就可以获得
我们考虑不要那么蠢所有点都枚举一次,因为当
这样我们就可以大概率通过此题(我的
点击查看代码
#include<bits/stdc++.h>
typedef long long LL;
typedef double DB;
using namespace std;
const int M=1e7+10;
int prime[M],d[M],cnt,n,k;
bool sf[M];
DB lg[M];
void init() {
sf[1]=1;
for(int i=2;i<=M-10;++i) {
if(!sf[i]) {
prime[++cnt]=i;
lg[i]=log(i); d[i]=i;
}
for(int j=1;j<=cnt;++j) {
int t=i*prime[j];
if(t>M-10) break;
sf[t]=1; d[t]=prime[j];
lg[t]=lg[i]+lg[prime[j]];
if(i%prime[j]==0) break;
}
}
}
const int MAXN=2100,inf=-1e9;
int a[MAXN],s[MAXN],b[MAXN],tot,pre[MAXN][MAXN];
int q[MAXN],h,t,kk[MAXN],pp;
DB f[MAXN][MAXN];
map<int,int> mp;
double cal(int i,int j,int p) {
return f[p-1][j]+lg[s[i]-s[j]];
}
int erfind(int x,int y,int i) {
int l=y,r=n+1,mid;
while(l+1<r) {
mid=(l+r)/2;
if(cal(mid,x,i)<=cal(mid,y,i)) r=mid;
else l=mid;
}
return l;
}
double ans;
int sta,res;
void calc(int st) {
tot=0;
for(int i=st;i<=n;++i) b[++tot]=a[i];
for(int i=1;i<st;++i) b[++tot]=a[i];
for(int i=1;i<=n;++i) {
s[i]=s[i-1]+b[i];
}
for(int i=0;i<=k;++i)
for(int j=1;j<=n;++j) f[i][j]=0;
f[0][0]=0;
for(int i=1;i<=k;++i) {
q[h=t=1]=i-1;
for(int j=0;j<=n;++j) kk[j]=0;
for(int j=i;j<=n;++j) {
while(h<t&&kk[h]<j) ++h;
f[i][j]=cal(j,q[h],i); pre[i][j]=q[h];
while(h<t&&kk[t-1]>=erfind(q[t],j,i)) --t;
if(res&&i==2&&j==2) pp=1;
kk[t]=erfind(q[t],j,i); pp=0;
q[++t]=j; kk[t]=0;
}
}
if(f[k][n]>ans) {
ans=f[k][n];
sta=st;
}
}
int ind[MAXN],len;
int main () {
srand(114514);
init();
while(scanf("%d%d",&n,&k)!=EOF) {
for(int i=1;i<=n;++i) {
scanf("%d",&a[i]);
a[n+i]=a[i];
}
int K=n/k*5; ans=0; sta=0;
for(int i=1;i<=K;++i) {
int st=rand()%n+1;
calc(st);
}
res=1;
calc(sta); res=0;
int xx=k,yy=n; len=0;
for(int i=k;i>=1;--i) {
ind[++len]=s[yy]-s[pre[i][yy]];
yy=pre[xx][yy];
--xx;
}
mp.clear();
for(int i=1;i<=len;++i) {
int ls=ind[i];
while(ls>1) {
++mp[d[ls]];
ls/=d[ls];
}
}
for(auto t:mp) {
printf("%d %d\n",t.first,t.second);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】