2022.9.21测试
一测:180pts
T1:P4171 [JSOI2010] 满汉全席(紫)(30pts)
T2:P4047 [JSOI2010]部落划分(绿)(100pts)
T3:P4048 [JSOI2010]冷冻波(紫)(50pts)
T4:P7221 [JSOI2010] 蔬菜庆典(灰=CTSC+/IOI/IOI+)(0pts)
也只能做做绿题了。
T1:
听说考场上有人随机化60pts。
做法:2-SAT。
2-SAT是一种基于 \(A\lor B\) 求解的算法,基本思路为:
若 \(A\) 不成立,则选 \(B\)。
若 \(B\) 不成立,则选 \(A\)。
那么就可以进行建边转化为图上问题,即建立 \(\lnot A\hspace{0.1cm}->B\) 和 \(\lnot B\hspace{0.1cm}->A\) 两条有向边。
若有子图 \(E\in G\) 为强连同分量,那在图 \(E\) 中的所有点满足 \(A\lor B\)。
本题即为2-SAT模板题。
对于每一个评审员的要求,若要求 \(A,B\) 满足 \(A\lor B\),则可以满足一个评审员。
\(\lnot A\) 可以理解为选择与自己不同的料理。
时间复杂度 \(O(n+m)\)
#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
using namespace std;
const int N=205;
const int M=1005;
vector<int>a[N];
int n,m,dfn[N],col[N],low[N],cnt,color,ins[N];
stack<int>s;
void Tarjan(int x)
{
dfn[x]=low[x]=++cnt;
s.push(x);
ins[x]=1;
int len=a[x].size();
for(int i=0;i<len;i++)
{
if(!dfn[a[x][i]])Tarjan(a[x][i]);
if(ins[a[x][i]])low[x]=min(low[x],low[a[x][i]]);
}
if(low[x]==dfn[x])
{
color++;
while(s.top()!=x)
{
col[s.top()]=color;
ins[s.top()]=0;
s.pop();
}
col[s.top()]=color;
ins[s.top()]=0;
s.pop();
}
}
int main()
{
//freopen("eat.in","r",stdin);
//freopen("eat.out","w",stdout);
int T;
scanf("%d",&T);
for(int i=1;i<=T;i++)
{
scanf("%d%d",&n,&m);
color=cnt=0;
while(!s.empty())s.pop();
for(int i=1;i<=2*n;i++)dfn[i]=col[i]=low[i]=ins[i]=0,a[i].clear();
for(int i=1;i<=m;i++)
{
char s1,s2;
int u1,v1;
scanf("%c",&s1);
while(s1!='m'&&s1!='h')scanf("%c",&s1);
scanf("%d",&u1);
scanf("%c",&s2);
while(s2!='m'&&s2!='h')scanf("%c",&s2);
scanf("%d",&v1);
int u2=s1=='m',v2=s2=='m';
a[u1+(u2^1)*n].push_back(v1+v2*n);
a[v1+(v2^1)*n].push_back(u1+u2*n);
}
for(int i=1;i<=2*n;i++)if(!dfn[i])Tarjan(i);
bool flag=1;
for(int i=1;i<=n;i++)if(col[i]==col[i+n])flag=0;
if(flag)printf("GOOD");
else printf("BAD");
putchar('\n');
}
}
/*
1
12 14
h1 m8
h7 h12
m9 h3
h11 m6
m10 h2
h11 m9
h6 h12
h8 m12
m12 h1
h7 h3
h12 h6
m2 m12
h1 m4
m1 h9
*/
T2:
由肉眼可见,最小生成树,没了。
对于最短的两个人之间的距离,如果两人不为一个种族,那么所有种族的最短边一定是这条边,那将这条边两人种族合并即可。
每合并两人种族,种族量减一,当种族量为 \(k\) 时,下一次需要合并的边即为答案。
复杂度 \(O(n^2\times \log{n^2})\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1005;
int n,k,x[N],y[N],cnt=0,f[N];
struct node
{
int from,to,data;
}a[N*N];
int cmp(node fi,node se)
{
return fi.data<se.data;
}
int afind(int x)
{
if(x==f[x])return x;
return f[x]=afind(f[x]);
}
int krus()
{
sort(a+1,a+1+cnt,cmp);
int num=n;
for(int i=1;i<=cnt;i++)
{
if(afind(a[i].from)==afind(a[i].to))continue;
num--;
if(num<k)return a[i].data;
f[afind(a[i].from)]=afind(a[i].to);
}
return 0;
}
int main()
{
//freopen("group.in","r",stdin);
//freopen("group.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&y[i]),f[i]=i;
for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)a[++cnt]=(node){i,j,(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])};
double ans=sqrt(1.0*krus());
printf("%.2lf",ans);
return 0;
}
T3:
首先很好想到我们应该预处理出来每一个巫妖王能攻击到的精灵。
那么这就是一个几何题。
对于每一组精灵与巫妖王,设巫妖王坐标为 \((x_1,y_1)\),精灵坐标为 \((x_2,y_2)\)。不考虑树的影响,若巫妖王要看到精灵,那么得满足:
\((x_1-x_2)^2+(y_1-y_2)^2\le r^2\)
用勾股定理或是两点间距离公式可以推出。
那么现在有了树的影响,对于每一棵树,我们都得计算它是否会遮挡视野。
如果一棵树会遮挡视野,当且仅当树经过了精灵与巫妖王的连线。
可以利用解析式求解。
精灵与巫妖王的连线斜率为:\(k_1=\frac{y_1-y_2}{x_1-x_2}\)。
代入直线解析式 \(y=k_1x+b_1\) 得截距。
得到解析式后要求树是否会挡路,那么就要求树到这儿的最短距离,即垂线段长度,这里依然用的解析法。
垂线段斜率:\(k_2=-\frac{1}{k_1}\)
垂线段截距:
最后解交点坐标 \((x_3,y_3)\):
如果树会挡路当且仅当交点在线段上,且交点距离树的距离小于半径 \(r_2\)。
若交点在线段上,那么 \(x_3\ge\min(x_1,x_2)\) 且 \(x_3\le\max(x_1,x_2)\)
若距离小于半径,设树的坐标为 \((x_4,y_4)\)。
那么 \(\sqrt{(x_4-x_3)^2+(y_4-y_3)^2}<r_2\)
由于此时坐标为浮点数,可以利用开方解决。
预处理做完后,就开始求时间花费了。
如果抛开冷却不谈,而是求本题有无解,仅仅是问对于每一个精灵是否能有一个巫妖王与之匹配。
如果再限制每个巫妖王施法次数,就是一个最大流问题。
连接源点与每一个巫妖王,边权是该巫妖王可攻击次数。再连接每一个巫妖王与可以攻击到的精灵,边权赋为 \(1\),因为一个巫妖王只能攻击一次相同的精灵。最后连接每个精灵与汇点,边权为 \(1\),因为每个精灵只能死一次。最后跑最大流即可得出是否有解。
现在有了冷却时间,实际上在一定时间 \(time\) 内第 \(i\) 个巫妖王可攻击次数为 \(time\div t_i+1\)。
而本题答案满足单调性,所以可以二分时答案,找到每个巫妖王的攻击次数,每一次跑一个最大流,就可以得到答案了。
约等于二分图匹配所以复杂度为 \(O((n+m)\times\sqrt {n+m})\)。
预处理复杂度为 \(O(nmk)\)。
#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
const int N=505;
int n,m,k,num[N],dep[N],vis[N];
struct node
{
int to,data;
};
vector<node>t[N];
vector<int>p[N];
struct wyw
{
int x,y,r,t;
}a[N];
struct xjl
{
int x,y;
}b[N];
struct tree
{
int x,y,r;
}c[N];
inline int tabs(int x)
{
return x>0?x:-x;
}
bool bfs()
{
memset(dep,0,sizeof(dep));
queue<int>q;
q.push(0);
dep[0]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
int len=t[x].size();
for(int i=0;i<len;i++)
{
if(t[x][i].data&&!dep[t[x][i].to])dep[t[x][i].to]=dep[x]+1,q.push(t[x][i].to);
}
}
if(dep[n+m+1])return true;
return false;
}
int dfs(int x,int num)
{
if(x==n+m+1)return num;
if(vis[x])return 0;
vis[x]=1;
int len=t[x].size();
for(int i=0;i<len;i++)
{
if(t[x][i].data&&dep[t[x][i].to]==dep[x]+1)
{
int res=dfs(t[x][i].to,min(num,t[x][i].data));
if(res)
{
t[x][i].data-=res;
t[t[x][i].to][p[x][i]].data+=res;
return res;
}
}
}
return 0;
}
int main()
{
//freopen("cold.in","r",stdin);
//freopen("cold.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].r,&a[i].t),num[i]=-a[i].t;
for(int i=1;i<=m;i++)scanf("%d%d",&b[i].x,&b[i].y);
for(int i=1;i<=k;i++)scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].r);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if((a[i].x-b[j].x)*(a[i].x-b[j].x)+(a[i].y-b[j].y)*(a[i].y-b[j].y)>a[i].r*a[i].r)continue;
bool flag=1;
for(int q=1;q<=k;q++)
{
double xielv=1.0*(a[i].y-b[j].y)/(1.0*(a[i].x-b[j].x));
if(a[i].x==b[j].x)xielv=1e-15;
double jieju=a[i].y-1.0*a[i].x*xielv;
double xl=-1.0/xielv;
double jj=c[q].y-1.0*c[q].x*xl;
double xx=(jj-jieju)/(xielv-xl);
double yy=xx*xielv+jieju;
if(xx+1e-5>min(a[i].x,b[j].x)&&xx-1e-5<max(a[i].x,b[j].x)&&sqrt((xx-c[q].x)*(xx-c[q].x)+(yy-c[q].y)*(yy-c[q].y))-1e-5<c[q].r)flag=0;
}
if(flag)
{
t[i].push_back((node){j+n,1}),t[j+n].push_back((node){i,0});
p[i].push_back(t[j+n].size()-1),p[j+n].push_back(t[i].size()-1);
}
}
t[i].push_back((node){0,0});
p[0].push_back(t[i].size()-1);
}
for(int i=1;i<=m;i++)
{
t[i+n].push_back((node){1+n+m,1}),t[1+n+m].push_back((node){i,0});
p[n+m+1].push_back(t[i+n].size()-1),p[i+n].push_back(t[n+m+1].size()-1);
}
for(int i=1;i<=n;i++)p[i].push_back(0);
int l=0,r=1e9;
while(l<r)
{
int mid=(l+r)>>1;
t[0].clear();
for(int i=1;i<=n;i++)
{
t[0].push_back((node){i,mid/a[i].t+1});
p[i][p[i].size()-1]=t[0].size()-1;
}
for(int i=1;i<=n+m;i++)
{
int len=t[i].size();
for(int j=0;j<len;j++)
{
if(i<t[i][j].to)t[i][j].data=1;
else t[i][j].data=0;
}
}
int ans=0;
while(bfs())
{
memset(vis,0,sizeof(vis));
int sum=dfs(0,1e9);
while(sum)
{
ans+=sum;
memset(vis,0,sizeof(vis));
sum=dfs(0,1e9);
}
}
if(ans==m)r=mid;
else l=mid+1;
}
int mid=l;
t[0].clear();
for(int i=1;i<=n;i++)
{
t[0].push_back((node){i,mid/a[i].t+1});
p[i][p[i].size()-1]=t[0].size()-1;
}
for(int i=1;i<=n+m;i++)
{
int len=t[i].size();
for(int j=0;j<len;j++)
{
if(i<t[i][j].to)t[i][j].data=1;
else t[i][j].data=0;
}
}
int ans=0;
while(bfs())
{
memset(vis,0,sizeof(vis));
int sum=dfs(0,1e9);
while(sum)
{
ans+=sum;
memset(vis,0,sizeof(vis));
sum=dfs(0,1e9);
}
}
if(ans!=m)printf("-1");
else printf("%d",l);
return 0;
}
T4:
通过分析样例,我们可以发现如果一个节点有多个 Dlihc,那么这些 Dlihc 对应的权值必须一样,否则可以无限延伸下去。
因为一号节点没有 Tnerap,所以一号节点一定不能更新,那我们可以看成一个根节点把一棵树分成了几个子任务。
若子树不为链,就一定有一个节点有多个 Dlihc,设这个节点为 \(x\)。
那如果要判断无解:
1、\(x\) 的子节点有权值不相同。
2、\(x\) 的子节点相同,且子节点有子节点,并且此子任务中有节点可以更新。
证明:第一个易证,第二个若 \(x\) 的子节点有子节点,代表 \(x\) 的子节点可以更新。若子节点的子节点的权值与 \(x\) 节点权值加起来为子节点权值相同,才有可能有解。若此时 \(x\) 节点和子节点的子节点更新,那么必定无解。若不能更新,那可能有解,但若 \(x\) 的父节点与子节点的子节点的子节点可以更新,那无解。综上,只要这个字任务的树中有节点可以更新,那么就无解。
然后就是求答案,首先如果一个子任务不为链且 \(x\) 的子节点有子节点,那么无法更新,直接累加即可。
若不为链但 \(x\) 的子节点无子节点,取任意一个子节点即可,化作为链求解。
求解过程与其他人差不多,对链上的数进行查分,将差分后的数从大到小排序后,求所有的差分前缀和即可。
程序中先将所有点的初始值加起来,如果要进行差分加排序操作,再减去。
flag 代表是否为不是链的情况,lim 代表是否能更新,flag2 代表 \(x\) 的子节点是否有子节点。
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
const int N=2e5+5;
int n,pre[N],num[N],ans,sum[N],pef[N];
vector<int>suf[N];
bool flag,lim,flag2;
bool dfs(int x,int step)
{
sum[step]=num[x]-num[pre[x]];
pef[step]=x;
int len=suf[x].size();
if(len==0&&!flag)
{
sort(sum+1,sum+1+step);
int tac=num[1];
for(int i=step;i>0;i--)tac+=sum[i],ans+=tac-num[pef[i]];
return true;
}
if(flag==1&&len!=0)flag2=1;
if(flag2==1&&lim==1)return false;
if(len>1)flag=1;
bool ss=1;
for(int i=0;i<len;i++)
{
if(num[pre[x]]+num[suf[x][i]]!=2*num[x])lim=1;
ss&=dfs(suf[x][i],step+1);
if(num[suf[x][0]]!=num[suf[x][i]])return false;
}
if(len>1&&!flag2)
{
sort(sum+1,sum+1+step+1);
int tac=num[1];
for(int i=step+1;i>0;i--)tac+=sum[i],ans+=tac-num[pef[i]];
}
return ss;
}
signed main()
{
//freopen("cele.in","r",stdin);
//freopen("cele.out","w",stdout);
while(1)
{
scanf("%lld",&n);
if(n==0)break;
ans=0;
for(int i=1;i<=n;i++)suf[i].clear(),scanf("%lld%lld",&pre[i],&num[i]),suf[pre[i]].push_back(i),ans+=num[i];
int len=suf[1].size();
bool st=1;
for(int i=0;i<len;i++)
{
flag=flag2=lim=0;
if(!dfs(suf[1][i],1))st=0;
}
if(!st)printf("+inf\n");
else printf("%lld\n",ans);
}
return 0;
}
/*
4
-1 0
1 3
2 2
2 2
*/

浙公网安备 33010602011771号