hdu-4781-Assignment For Princess
problem
1.让你构造一个图,下面是这个图的样子。
2.n个点m条边,每条边是有向边,权值是[1, m],任意两个边的权值都不一样。n[10, 80], m[n+3, n^2/7]
3.没有子环,任两个点之间最多有一条边(这句话的意思是,两个点之间直接相连的边,最多只有一条,不同方向的也算两条)。
4.任意两个点之间都是可以到的。
5.每个点出发,都可以回到这个点,并且每个一圈的权值和是3的倍数。
think
1.先把1到n做成一个圈,这样用了n个点。其中前几个边的权值是1-(n-1), 最后一个点是n或n+2,为了满足一圈的和是%3==0嘛。
2.这样任意两个点之间都有了唯一的距离,加权值为k的边的时候,只要dis[i][j] % 3 == k % 3就可以加上去了。
code
int dis[90][90];
int has[90][90];
int vis[1000];
int val[90];
int a[1000], b[1000], c[1000];
int cnt;
bool solve(int n, int m){
memset(vis, 0, sizeof(vis));
memset(has, 0, sizeof(has));
int x;
if(n % 3 == 1) x = n + 2;
else x = n;
for(int i = 1; i < n; ++i){
vis[i] = 1;
val[i] = i;
}
vis[x] = 1;
val[n] = x;
for(int i = 1; i <= n; ++i){
int ii = i + 1;
if(ii > n) ii = 1;
a[++cnt] = i;
b[cnt] = ii;
c[cnt] = val[i];
has[i][i] = 1;
has[i][ii] = 1;
has[ii][i] = 1;
for(int j = 1; j <= n; ++j){
if(i == j) dis[i][j] = 0;
else if(i < j) dis[i][j] = (dis[i][j-1] + val[j-1]) % 3;
else dis[i][j] = (3 - dis[j][i]) % 3;
}
}
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= n; ++j){
if(has[i][j] || has[j][i]) continue;
for(int k = 1; k <= m; ++k){//这里可以优化为O(1),但是n和m这么小不需要优化也能过
if(!vis[k] && (k % 3 == dis[i][j])){
a[++cnt] = i;
b[cnt] = j;
c[cnt] = k;
vis[k] = 1;
has[i][j] = 1;
has[j][i] = 1;
break;
}
}
}
}
for(int i = 1; i <= m; ++i){
if(vis[i] == 0) return false;
}
return true;
}
int main(){
int T, tt = 0;
scanf("%d", &T);
while(T--){
int n, m;
cin >> n >> m;
printf("Case #%d:\n", ++tt);
cnt = 0;
if(solve(n, m)){
for(int i = 1; i <= m; ++i) printf("%d %d %d\n", a[i], b[i], c[i]);
} else {
puts("-1");
}
}
return 0;
}
hdu-4790-Just Random
problem
从区间[a, b]等概率选择一个数x,从区间[c, d]等概率选择一个数y,
问 (x+y) % p == m 的概率是什么。
给你a, b, c, d, p, m范围都是1e9
think
看有多少个(x+y) % p == m 的(x, y) 点对。结果除以(b - a + 1) * (d - c + 1)
可以把x和y看作坐标上面的两维。那么我们求的就是x + y == m, x + y == p + m , …… x + y == k*p + m 这些线段上面有多少个点。
我们可以求出x固定时,有多少个满足要求的y。
然后再求一求吧。
首先我们求的区间是左端是0的比较容易吧。于是我们定义一个函数,solve(a, b)表示第一维区间是[0, a], 第二维区间是[0, b]时满足条件的(x, y)点对数。
于是结果就是solve(b, d) - solve(b, c-1) - solve(a-1, d) + solve(a-1, c-1)
然后solve(a, b)这个函数。每个人有每个人不同的求法吧。我总觉得我的求法有点麻烦。。。
首先我们得到,这些线段们与y轴的交点个数。就是当x==0时。设这个数是n.
然后得到当y==b时,最小的x。设这个数为x。
讨论x的情况。
如果x<=m的话,就是在[x, m] 这个区间,会比别处多一个。加上就好。但是x==0的话,就把n减一再加。
如果x==m+1的话,就是在各处都是n。
如果x>m+1的话,就是在[x+1, m-1]比别处少一个。减去就好。
详见代码。
code
LL m, p;
LL solve(LL a, LL b){
if(a < 0 || b < 0) return 0;
LL n;
if(b >= m) n = (b - m) / p + 1;
else n = 0;
LL x;
if(m >= b) x = (m - b) % p;
else x = (m - b + (b - m) * p) % p;
LL ans = 0;
if(x == 0) --n;
ans += n*(a+1);
if(x <= m){
LL k = a / p;
ans += k*(m-x+1);
ans += min(a, k*p + m) - min(a, k*p + x - 1);
}
else if(x > m+1){
LL k = a / p;
ans -= k * (x - m - 1);
ans -= min(a, k*p + x - 1) - min(a, k*p + m + 1 - 1);
}
return ans;
}
int main(){
int T, tt = 0;
LL a, b, c, d;
scanf("%d", &T);
while(T--){
scanf("%I64d%I64d%I64d%I64d%I64d%I64d", &a, &b, &c, &d, &p, &m);
LL ans = solve(b, d) - solve(b, c-1) - solve(a-1, d) + solve(a-1, c-1);
LL sum = (b - a + 1) * (d - c + 1);
LL gg = __gcd(sum, ans);
ans /= gg;
sum /= gg;
printf("Case #%d: %I64d/%I64d\n", ++tt, ans, sum);
}
return 0;
}
/*
33333
0 5 0 5 3 0
0 999999 0 999999 1000000 0
0 3 0 3 8 7
3 3 4 4 7 0
0 5 0 0 3 1
0 5 0 1 3 1
0 5 0 2 3 1
0 5 0 3 3 1
0 5 0 4 3 1
0 5 0 5 3 1
0 5 0 6 3 1
0 5 0 7 3 1
0 5 0 8 3 1
*/