SJR的直线
题目大意
给你n条直线,每条直线的方程为Aix+Biy+Ci=0,求这些直线相交组成了多少个三角形
解法
注意,没有三条直线交于一点
对于所有的平行线,我们考虑他们对答案的影响分别是什么
考虑每次加入一条直线的影响,明显两条平行线和一条其他斜率的线不能满足题意,累加答案时应避开这种情况,于是可以想到加入一条线增加的三角形等于选了这条线并且在前面的i-1条中选两条的方案数 - 前面的重复方案数 再加上这次少加的t条中选两条的方案数。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#include<set>
using namespace std;
const int MAXN = 3e5 + 5;
const int MOD = 1e9 + 7;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
int n,a[MAXN],b[MAXN];
ll ans = 0,pre = 0;
map<pair<int,int>,int> line;
void in(int &x)
{
int num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void lin(ll &x)
{
ll num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void out(ll x)
{
if(x < 0) x = -x,pt('-');
if(x > 9) out(x/10);
pt(x % 10 + '0');
}
int gcd(int a,int b) {return b == 0 ? a : gcd(b,a%b);}
ll C(ll x) {return (x*(x-1)/2) % MOD;}
int main()
{
in(n); if(n < 3) {out(0); return 0;}
for(int i = 1;i <= n;i++)
{
int c;in(a[i]),in(b[i]),in(c);
int t = gcd(a[i],b[i]); a[i] /= t,b[i] /= t;
t = line[make_pair(a[i],b[i])]++;
ans = (ans + C(i-1-t) - pre + C(t) + MOD) % MOD;
pre = (pre + C(t+1) - C(t) + MOD) % MOD;
}
out(ans);
return 0;
}
三部曲
题目大意
因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为0。当城市i被加派了k名士兵时。城市i的所有子城市需要被加派k+1名士兵。这些子城市的所有子城市需要被加派k+2名士兵。以此类推。
q个操作:1. 给城市i增派k名士兵 2. 询问以i为根的子树的士兵总数。
n≤50000 q≤100000
分析
在子树上维护信息的题,一般可以考虑求出dfs序,然后用数据结构维护。
考虑一个增派操作的某一个节点x,如果它要增加k个人,它的儿子就都要增加k+1个人,然而儿子的深度都比x的深度大1。所以可以考虑把k和后面加的东西分开。
一个比较简单的思路:把增派k人拆成增派k’+dep(x)人。那么对于整个子树,前面的k’部分,全部节点加的是一样的。后面的无论操作有几次,加的都是dep(x),只要记录节点x被多少个插入操作覆盖即可。
用线段树维护即可。时间复杂度O(nlogn)
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#include<set>
using namespace std;
const int MAXN = 5e4 + 5;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
#define lx x << 1
#define rx x << 1 | 1
int n,p,fa[MAXN],dfn[MAXN];
int pos[MAXN],dep[MAXN];
ll siz[MAXN],son[MAXN],ans;
struct edge
{
int next,to;
}e[MAXN<<1];
int head[MAXN<<1],cnt = 0;
struct tree
{
int l,r;
ll sum,dep;
}t[MAXN<<2];
struct lazy
{
ll k,Times;
}lz[MAXN<<2];
void add(int u,int v)
{
e[++cnt].next = head[u]; e[cnt].to = v; head[u] = cnt;
}
void in(int &x)
{
int num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void lin(ll &x)
{
ll num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void out(ll x)
{
if(x < 0) x = -x,pt('-');
if(x > 9) out(x/10);
pt(x % 10 + '0');
}
void dfs(int x)
{
dfn[x] = ++cnt;
dep[x] = dep[fa[x]] + 1;
pos[cnt] = x;
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].to;
dfs(to); siz[x] += siz[to] + 1;
}
}
void build(int x,int l,int r)
{
t[x].l = l,t[x].r = r;
if(l == r)
{
t[x].dep = dep[pos[l]];
return;
}
int mid = (l + r) >> 1;
build(lx,l,mid); build(rx,mid+1,r);
t[x].dep = t[lx].dep + t[rx].dep;
}
void pushdown(int x)
{
if(lz[x].k != 0)
{
lz[lx].k += lz[x].k;
t[lx].sum += lz[x].k * (t[lx].r - t[lx].l + 1);
lz[rx].k += lz[x].k;
t[rx].sum += lz[x].k * (t[rx].r - t[rx].l + 1);
lz[x].k = 0;
}
if(lz[x].Times != 0)
{
lz[lx].Times += lz[x].Times;
t[lx].sum += lz[x].Times*t[lx].dep;
lz[rx].Times += lz[x].Times;
t[rx].sum += lz[x].Times*t[rx].dep;
lz[x].Times = 0;
}
}
void updata(int x,int l,int r,ll k)
{
if(t[x].l == l && t[x].r == r)
{
lz[x].Times++; lz[x].k += k;
t[x].sum += t[x].dep + k*(t[x].r - t[x].l + 1);
return;
}
pushdown(x);
if(r <= t[lx].r) updata(lx,l,r,k);
else if(l >= t[rx].l) updata(rx,l,r,k);
else updata(lx,l,t[lx].r,k),updata(rx,t[rx].l,r,k);
t[x].sum = t[lx].sum + t[rx].sum;
}
void ask(int x,int l,int r)
{
if(t[x].l == l && t[x].r == r)
{
ans += t[x].sum; return;
}
pushdown(x);
if(r <= t[lx].r) ask(lx,l,r);
else if(l >= t[rx].l) ask(rx,l,r);
else ask(lx,l,t[lx].r),ask(rx,t[rx].l,r);
}
int main()
{
in(n),in(p);
for(int i = 2;i <= n;i++)
{
int fr; in(fr);
fa[i] = fr; add(fr,i);
}
cnt = 0; dfs(1);
build(1,1,n);
for(int i = 1;i <= p;i++)
{
char ch = gc(); while(ch == '\n') ch = gc();
int x; ll v; in(x);
if(ch == 'A')
{
lin(v);
updata(1,dfn[x],dfn[x] + siz[x],v - dep[x]);
// for(int i = 1;i <= n;i++) ans = 0,ask(1,dfn[x],dfn[x] + siz[x]),out(ans),ko;
}
else if(ch == 'Q')
{
ans = 0;
ask(1,dfn[x],dfn[x] + siz[x]);
out(ans),ex;
}
}
return 0;
}
/*
7 10
1 1 2 2 5 5
Q 1
A 2 1
Q 1
Q 2
Q 5
A 5 0
Q 5
A 3 1
Q 1
Q 2
*/
兔子
【问题描述】
在一片草原上有N个兔子窝,每个窝里住着一只兔子,有M条路径连接这些窝。更特殊地是,至多只有一个兔子窝有3条或更多的路径与它相连,其它的兔子窝只有1条或2条路径与其相连。换句话讲,这些兔子窝之前的路径构成一张N个点、M条边的无向连通图,而度数大于2的点至多有1个。
兔子们决定把其中K个兔子窝扩建成临时避难所。当危险来临时,每只兔子均会同时前往距离它最近的避难所躲避,路程中花费的时间在数值上等于经过的路径条数。为了在最短的时间内让所有兔子脱离危险,请你安排一种建造避难所的方式,使最后一只到达避难所的兔子所花费的时间尽量少。
【输入】
第一行有3个整数N,M,K,分别表示兔子窝的个数、路径数、计划建造的避难所数。
接下来M行每行三个整数x,y,表示第x个兔子窝和第y个兔子窝之间有一条路径相连。任意两个兔子窝之间至多只有1条路径。
【输出】
一个整数,表示最后一只到达避难所的兔子花费的最短时间。
【输入输出样例1】
rabbit.in
5 5 2
1 2
2 3
1 4
1 5
4 5
rabbit.out
1
【输入输出样例1说明】
在第2个和第5个兔子窝建造避难所,这样其它兔子窝的兔子最多只需要经过1条路径就可以到达某个避难所。
【数据规模与约定】
对于30%的数据,N≤15,K≤4;
对于60%的数据,N≤100;
对于100%的数据,1≤K≤N≤1,000,1≤M≤1,500
分析
题意就不说了,注意到一个关键点是一个特殊节点:至多一个的度数大于等于3的点。
可以通过画图分析得到,设此度数大于等于3的点为根,这样的图只有从根出发的不分叉回路,就是说所有的环都是连在根上并且根为这些环的唯一交点。
考虑二分答案,根据二分的答案进行check合理性,用以二分出的最大距离用最少的避难所覆盖整个图,但是如果暴力枚举的话复杂度很高。
于是考虑图本身性质,前面说到度数大于等于3的结点作为根,可以想到,若先找到一个结点让根可以在二分出的当前答案的前提下被覆盖,那么相当于去掉那一片包括根的区域后,剩下的都是链了(由上可知所有环因为根的失去而变为链),于是就可以枚举哪一个结点来覆盖根,再算每条链所需的避难所个数。
覆盖根用一个避难所,每条链避难所数量应该是:设此链节点数为x,则避难所所需数量为x/(2*ans+1),上取整。(贪心的思想,让每个避难所尽量覆盖结点,每个避难所可以覆盖2*ans+1个节点,也就是建避难所的结点+左右两边可以覆盖的结点数,上取整是必须满足覆盖)
如此二分,判断当前答案下可否满足最小所用的k小于等于题目给定的k,最后二分出的就是答案。
复杂度O(n2logn)
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#include<set>
using namespace std;
const int MAXN = 3e3 + 5;
const int INF = 999999999;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
int n,m,k,ans = INF,tmp;
int ing[MAXN],vis[MAXN];
int dis[MAXN],st = 1;
struct edge
{
int next,to;
}e[MAXN<<1];
int head[MAXN<<1],cnt = 0,tot = 0,totk = 0;
void add(int u,int v)
{
e[++cnt].next = head[u]; e[cnt].to = v; head[u] = cnt;
e[++cnt].next = head[v]; e[cnt].to = u; head[v] = cnt;
}
void in(int &x)
{
int num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void lin(ll &x)
{
ll num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void out(int x)
{
if(x < 0) x = -x,pt('-');
if(x > 9) out(x/10);
pt(x % 10 + '0');
}
int q[MAXN<<2];
void bfs(int st)
{
int h = 0,t = 0;
q[++t] = st,vis[st] = 1,dis[st] = 0;
while(h < t)
{
int x = q[++h];
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].to;
if(vis[to]) continue;
dis[to] = dis[x] + 1;
vis[to] = 1;
q[++t] = to;
}
}
}
void calc(int x,int dep)
{
vis[x] = 1; if(!dep) return;
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].to;
if(vis[to]) continue;
calc(to,dep-1);
}
}
void cal(int x)
{
cnt++,vis[x] = 1;
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].to;
if(vis[to]) continue;
cal(to);
}
}
bool check(int len)
{
for(int i = 1;i <= n;i++)
{
if(dis[i] <= len)
{
memset(vis,0,sizeof vis);
calc(i,len); totk = 1;
for(int j = 1;j <= n;j++)
{
if(vis[j]) continue;
cnt = 0; cal(j);
totk += cnt/(len*2+1);
if(cnt % (len*2+1)) totk++;
}
if(totk <= k) return 1;
}
}
return 0;
}
int main()
{
in(n),in(m),in(k);
if(k >= n) {out(0); return 0;}
for(int i = 1;i <= m;i++)
{
int x,y; in(x),in(y);
add(x,y); ing[x]++,ing[y]++;
}
for(int i = 1;i <= n;i++)
if(ing[i] >= 3) {st = i; break;}
bfs(st);
int l = 0,r = n;
while(l < r)
{
int mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
out(l);
return 0;
}
/*
5 5 2
1 2
2 3
1 4
1 5
4 5
*/