ConvexScore
题目分析
神奇组合题
题目中这个 (m−left| S ight|) 相当于求凸多边形内顶点外其他点的数量
观察发现这个 (2^{m−left| S ight|}) 十分奇怪,考虑它有什么特殊的含义
我们发现,这个 (2^{m−left| S ight|}) 代表凸多边形内除顶点外所有点都有选与不选两种状态
思考后发现答案好像就是能组成的多边形的数量
容斥一下,多边形的数量即为所有点集数量减去不能组成多边形的点集的数量
所有点集数量就是 (2^n) ,不能组成多边形的点集的数量就是包含 (0,1,2) 个点的点集数量与多点共线的点集数量
包含 (0,1,2) 个点的点集数量就是 (C_n^0+C_n^1+C_n^2) ,多点共线的点集数量也很好求,我们枚举线段的两端,再枚举在这条线段上的点的数量 (cnt) ,那么在这条线段上的点都有选和不选两种状态,不能全部不选(这样就只有两个点了),所以这条线段的贡献就是 (2^{cnt}-1)
注意特判 (n<3) 的情况
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int mod=998244353;
int n,x[205],y[205];
long long fpow(long long x,int y) {
long long res=1;
while(y) {
if(y&1)res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
double slope(int i,int j) {
if(x[i]==x[j])return -1e18;
return (double)(y[i]-y[j])/(x[i]-x[j]);
}
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%d%d",&x[i],&y[i]);
}
if(n<3) {
printf("0");
return 0;
}
long long ans=(fpow(2,n)-1-n-n*(n-1)/2+mod)%mod;
for(int i=1; i<=n; i++) {
for(int j=i+1; j<=n; j++) {
int cnt=0;
for(int k=j+1; k<=n; k++) {
if(slope(i,k)==slope(k,j))cnt++;
}
ans=(ans-fpow(2,cnt)+1+mod)%mod;
}
}
printf("%lld",ans);
return 0;
}
Poor Turkeys
题目分析
枚举点对 ((x,y)) ,表示询问鸡 (x) 和鸡 (y) 能否同时存活
我们发现,若鸡 (x) 想要存活,那么对于第 (i) 次吃鸡活动 ((x,z)) (即所有 (a_i=x) 或 (b_i=x) 的吃鸡活动),鸡 (z) 必死无疑,且在前 (i-1) 次吃鸡活动中鸡 (z) 不能死去
同理,对于第 (jleft(1le j<i ight)) 次吃鸡活动 ((z,z_2)) ,鸡 (z_2) 必死无疑,且在前 (j-1) 次吃鸡活动中鸡 (z_2) 不能死去
依此类推,直到前 (i-1) 次活动中没有与 (z) 有关的吃鸡活动时搜索结束
设 (t_u) 为编号为 (u) 的鸡在第 (t_u) 次吃鸡活动时必死无疑,且在前 (t_u-1) 次吃鸡活动中鸡 (u) 不能死去
分别对鸡 (x) 和鸡 (y) 进行搜索,当搜索到鸡 (u) 时如果发现 (t_u) 已有值且不为当前值时矛盾,鸡 (x) 和鸡 (y) 不能同时存活,否则鸡 (x) 和鸡 (y) 能同时存活
时间复杂度 (O(n^2m)) ,但似乎卡不满
考虑预处理,时间复杂度可以优化到 (O(nm+n^3)) ,稳过
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
struct edge {
int to,nxt,val;
} e[200005];
int tot=-1,head[405];
void addedge(int u,int v,int w) {
e[++tot].to=v;
e[tot].val=w;
e[tot].nxt=head[u];
head[u]=tot;
}
int t[405][405];
int die[405];
bool vis[405];
void dfs(int rt,int u,int fa) {
vis[u]=true;
for(int i=head[u]; ~i; i=e[i].nxt) {
if((i^1)==fa)continue;
int v=e[i].to,w=e[i].val;
if(w<t[rt][u]) {
if(t[rt][v]==0)t[rt][v]=w;
else if(t[rt][v]!=w)die[rt]=1;
if(!vis[v])dfs(rt,v,i);
}
}
}
bool check(int x,int y) {
if(die[x]||die[y])return false;
for(int i=1; i<=n; i++) {
if(t[x][i]&&t[y][i]) {
if(t[x][i]!=t[y][i])return false;
}
}
return true;
}
int main() {
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v,i);
addedge(v,u,i);
}
for(int i=1; i<=n; i++) {
memset(vis,0,sizeof(vis));
t[i][i]=m+1;
dfs(i,i,-1);
}
int ans=0;
for(int i=1; i<=n; i++) {
for(int j=i+1; j<=n; j++) {
if(check(i,j))ans++;
}
}
printf("%d",ans);
return 0;
}
Pustynia
题目思路
有技巧的差分约束题
弄清题意后,很容易想到这是一道差分约束题,然而直接建边 (O(n^3)) 明显爆炸,考虑优化
采用线段树优化建图,将 (k) 个点连向 (k+1) 个区间,区间用线段树优化分成 (logn) 个区间,大的区间再向小的区间连
但 (O(n^2logn)) 仍会爆炸,使用一个常见的trick,新建一个中转节点, 将 (k) 个点连向这个中转节点,边权为一,将这个中转节点连向 (k+1) 个区间,边权为 (0) ,这样就能轻易做到 (O(nlogn)) 了
发现这样一个图如果存在环显然是 NIE
的,因此我们只用在有向无环图上进行操作,直接拓扑排序即可,不用最短路
代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
int n,m1,m2,a[600005],a2[600005];
int d[600005];
struct edge {
int to,nxt,val;
} e[10000005];
int tot,head[600005];
void addedge(int u,int v,int w) {
d[v]++;
e[++tot].to=v;
e[tot].val=w;
e[tot].nxt=head[u];
head[u]=tot;
}
int cnt,id[400005];
void build(int u,int l,int r) {
if(l==r) {
id[u]=l;
return;
}
id[u]=++cnt;
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
addedge(id[u],id[u<<1],0);
addedge(id[u],id[u<<1|1],0);
}
void update(int u,int l,int r,int x,int y,int v) {
if(x>y)return;
if(x<=l&&r<=y) {
addedge(v,id[u],0);
return;
}
int mid=(l+r)>>1;
if(x<=mid)update(u<<1,l,mid,x,y,v);
if(y>mid)update(u<<1|1,mid+1,r,x,y,v);
}
bool bfs() {
queue<int>q;
for(int i=1; i<=cnt; i++) {
a[i]=1e9;
if(d[i]==0) {
if(a2[i])a[i]=a2[i];
q.push(i);
}
}
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=head[u]; i; i=e[i].nxt) {
int v=e[i].to,w=e[i].val;
if(a2[v]==0) {
a[v]=min(a[v],a[u]-w);
if(a[v]==0)return false;
} else {
if(a[u]-w>=a2[v])a[v]=a2[v];
else return false;
}
d[v]--;
if(d[v]==0) {
q.push(v);
}
}
}
for(int i=1; i<=cnt; i++) {
if(d[i])return false;
}
return true;
}
int main() {
scanf("%d%d%d",&n,&m1,&m2);
for(int i=1; i<=m1; i++) {
int x,y;
scanf("%d%d",&x,&y);
a2[x]=y;
}
cnt=n;
build(1,1,n);
for(int i=1; i<=m2; i++) {
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
cnt++;
int last=l;
for(int j=1; j<=k; j++) {
int now;
scanf("%d",&now);
addedge(now,cnt,1);
update(1,1,n,last,now-1,cnt);
last=now+1;
}
update(1,1,n,last,r,cnt);
}
if(bfs()) {
printf("TAK
");
for(int i=1; i<=n; i++) {
printf("%d ",a[i]);
}
} else printf("NIE");
return 0;
}