SDOI2009
Posted Ra1nbow
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDOI2009相关的知识,希望对你有一定的参考价值。
1226: [SDOI2009]学校食堂Dining
Description
小F 的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭。学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴。当然,不同的人口味也不一定相同,但每个人的口味都可以用一个非负整数表示。由于人手不够,食堂每次只能为一个人做菜。做每道菜所需的时间是和前一道菜有关的,若前一道菜的对应的口味是a,这一道为b,则做这道菜所需的时间为(a or b)-(a and b),而做第一道菜是不需要计算时间的。其中,or 和and 表示整数逐位或运算及逐位与运算,C语言中对应的运算符为“|”和“&”。学生数目相对于这个学校还是比较多的,吃饭做菜往往就会花去不少时间。因此,学校食堂偶尔会不按照大家的排队顺序做菜,以缩短总的进餐时间。虽然同学们能够理解学校食堂的这种做法,不过每个同学还是有一定容忍度的。也就是说,队伍中的第i 个同学,最多允许紧跟他身后的Bi 个人先拿到饭菜。一旦在此之后的任意同学比当前同学先拿到饭,当前同学将会十分愤怒。因此,食堂做菜还得照顾到同学们的情绪。现在,小F 想知道在满足所有人的容忍度这一前提下,自己的学校食堂做完这些菜最少需要多少时间。
Input
第一行包含一个正整数C,表示测试点的数据组数。每组数据的第一行包含一个正整数N,表示同学数。每组数据的第二行起共N行,每行包含两个用空格分隔的非负整数Ti和Bi,表示按队伍顺序从前往后的每个同学所需的菜的口味和这个同学的忍受度。每组数据之间没有多余空行。
Output
包含C行,每行一个整数,表示对应数据中食堂完成所有菜所需的最少时间。
Sample Input
2
5
5 2
4 1
12 0
3 3
2 2
2
5 0
4 0
Sample Output
16
1
HINT
对于第一组数据:同学1允许同学2或同学3在他之前拿到菜;同学2允许同学3在他之前拿到菜;同学3比较小气,他必须比他后面的同学先拿菜…… 一种最优的方案是按同学3、同学2、同学1、同学4、同学5做菜,每道菜所需的时间分别是0、8、1、6及1。 【数据规模和约定】对于30%的数据,满足1 ≤ N ≤ 20。对于100%的数据,满足1 ≤ N ≤ 1,000,0 ≤ Ti ≤ 1,000,0 ≤ Bi ≤ 7,1 ≤ C ≤ 5。存在30%的数据,满足0 ≤ Bi ≤ 1。存在65%的数据,满足0 ≤ Bi ≤ 5。存在45%的数据,满足0 ≤ Ti ≤ 130。
由于$0\\leq B_i\\leq 7$,很容易想到状压dp,那么正解就很清晰了..
$f_{i,j,k}$表示到第$i$人(前提:$1...i$的人都完成了),包括$i$后面$7$人的状态,前一个完成的人与$i$的相对位置是$k(-8...7)$(注意是$-8$啊卧槽)
然后就乱搞转移就行了..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 1010;
const int inf = 1e9;
const int N = 8;
int f[Maxn][(1<<9)-1][16];
int t[Maxn], b[Maxn], n;
int _min ( int x, int y ){ return x < y ? x : y; }
int cost ( int x, int y ){ return x == 0 ? 0 : t[x]^t[y]; }
int main (){
int i, j, k, T;
scanf ( "%d", &T );
while ( T -- ){
scanf ( "%d", &n );
for ( i = 1; i <= n; i ++ ) scanf ( "%d%d", &t[i], &b[i] );
b[0] = 7;
memset ( f, 63, sizeof (f) ); f[0][1][N] = 0;
for ( i = 0; i <= n; i ++ ){
for ( j = 0; j < (1<<(b[0]+1)); j ++ ){
for ( k = 0; k <= N+b[0]; k ++ ){
if ( f[i][j][k] > inf ) continue;
if ( j & 1 ) f[i+1][j>>1][k-1] = _min ( f[i+1][j>>1][k-1], f[i][j][k] );
else {
int r = inf;
for ( int l = 0; l <= b[i]; l ++ ){
if ( i+l > r ) break;
if ( j & (1<<l) ) continue;
r = _min ( r, i+l+b[i+l] );
f[i][j+(1<<l)][N+l] = _min ( f[i][j+(1<<l)][N+l], f[i][j][k]+cost(i+k-N,i+l) );
}
}
}
}
}
int ans = inf;
for ( i = 0; i < N; i ++ ) ans = _min ( ans, f[n+1][0][i] );
printf ( "%d\\n", ans );
}
return 0;
}
1227: [SDOI2009]虔诚的墓主人
Description
小W 是一片新造公墓的管理人。公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地。当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地。为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度。一块墓地的虔诚度是指以这块墓地为中心的十字架的数目。一个十字架可以看成中间是墓地,墓地的正上、正下、正左、正右都有恰好k 棵常青树。小W 希望知道他所管理的这片公墓中所有墓地的虔诚度总和是多少
Input
第一行包含两个用空格分隔的正整数N 和M,表示公墓的宽和长,因此这个矩形公墓共有(N+1) ×(M+1)个格点,左下角的坐标为(0, 0),右上角的坐标为(N, M)。第二行包含一个正整数W,表示公墓中常青树的个数。第三行起共W 行,每行包含两个用空格分隔的非负整数xi和yi,表示一棵常青树的坐标。输入保证没有两棵常青树拥有相同的坐标。最后一行包含一个正整数k,意义如题目所示。
Output
包含一个非负整数,表示这片公墓中所有墓地的虔诚度总和。为了方便起见,答案对2,147,483,648 取模。
Sample Input
5 6
13
0 2
0 3
1 2
1 3
2 0
2 1
2 4
2 5
2 6
3 2
3 3
4 3
5 2
2Sample Output
6HINT
图中,以墓地(2, 2)和(2, 3)为中心的十字架各有3个,即它们的虔诚度均为3。其他墓地的虔诚度为0。
所有数据满足1 ≤ N, M ≤ 1,000,000,000,0 ≤ xi ≤ N,0 ≤ yi ≤ M,1 ≤ W ≤ 100,000, 1 ≤ k ≤ 10。存在50%的数据,满足1 ≤ k ≤ 2。存在25%的数据,满足1 ≤ W ≤ 10000。
注意:”恰好有k颗树“,这里的恰好不是有且只有,而是从>=k的树中恰好选k棵
这道题就很搞笑了.. 模什么不好非要模$2^{31}$,真的是调这sb调了我半天..
按照常理首先要离散化
考虑一种类似于扫描线的做法,扫描$x=k$事件时,用树状数组(或者线段树吧如果你不怕常数大)统计在点$(k,y)$中$(x,y),x<k$和$(x,y),x>k$对这个点产生的影响
这样总体复杂度就是$O(nlogn)$的..
给大家讲个道理,你还是要预处理出$C_x^K$的吧..(不预处理也行,你喜欢每次都算算也不是不行)
这个东西还要在模$2^{31}$下的,也就是tm就是没逆元
太高级的我都不会。于是蒟蒻的我使用了一种$O(nK^2)$的sb做法,就是每次都进行一次手动约分..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
const LL Maxn = 100010;
const LL Mod = (LL)1<<31;
LL bl[Maxn], bll, bc[Maxn], bcl;
LL sum[Maxn], now[Maxn], C[Maxn], suml[Maxn], nl[Maxn];
struct node {
LL x, y;
}list[Maxn]; LL n, m, tot, K;
bool cmp ( node x, node y ){
if ( x.x == y.x ) return x.y < y.y;
return x.x < y.x;
}
LL exgcd ( LL a, LL b, LL &x, LL &y ){
if ( a == 0 ){
x = 0; y = 1;
return b;
}
LL tx, ty;
LL d = exgcd ( b%a, a, tx, ty );
y = tx;
x = ty-(b/a)*tx;
return d;
}
LL gcd ( LL a, LL b ){
return a == 0 ? b : gcd ( b%a, a );
}
LL BIT[Maxn];
LL lowbit ( LL x ){ return x & (-x); }
void change ( LL x, LL k ){
while ( x <= bcl ){
BIT[x] = (BIT[x]+k+Mod)%Mod;
x += lowbit (x);
}
}
LL query ( LL x ){
LL ret = 0;
while ( x > 0 ){
ret = (ret+BIT[x])%Mod;
x -= lowbit (x);
}
return ret;
}
int bo[11];
int main (){
LL i, j, k;
scanf ( "%lld%lld%lld", &n, &m, &tot );
for ( i = 1; i <= tot; i ++ ){
scanf ( "%lld%lld", &list[i].x, &list[i].y );
bl[++bll] = list[i].x; bc[++bcl] = list[i].y;
}
sort ( bl+1, bl+bll+1 );
bll = unique ( bl+1, bl+bll+1 ) - (bl+1);
sort ( bc+1, bc+bcl+1 );
bcl = unique ( bc+1, bc+bcl+1 ) - (bc+1);
for ( i = 1; i <= tot; i ++ ){
LL x = lower_bound ( bc+1, bc+bcl+1, list[i].y ) - bc;
sum[x] ++;
x = lower_bound ( bl+1, bl+bll+1, list[i].x ) - bl;
suml[x] ++;
}
sort ( list+1, list+tot+1, cmp );
scanf ( "%lld", &K );
for ( i = K; i <= tot; i ++ ){
LL ss = 1;
for ( j = 2; j <= K; j ++ ) bo[j] = j;
for ( j = i-K+1; j <= i; j ++ ){
int p = j;
for ( k = 2; k <= K; k ++ ){
if ( bo[k] == 1 ) continue;
int o = gcd ( p, bo[k] );
p /= o;
bo[k] /= o;
}
ss = (ss*p)%Mod;
}
C[i] = ss;
}
LL ans = 0;
for ( i = 1; i <= tot; i ++ ){
LL x = lower_bound ( bl+1, bl+bll+1, list[i].x ) - bl;
LL y = lower_bound ( bc+1, bc+bcl+1, list[i].y ) - bc;
change ( y, -(C[now[y]]*C[sum[y]-now[y]])%Mod );
now[y] ++; nl[x] ++;
change ( y, (C[now[y]]*C[sum[y]-now[y]])%Mod );
if ( list[i+1].x != list[i].x ) continue;
LL yy = lower_bound ( bc+1, bc+bcl+1, list[i+1].y ) - bc;;
ans = (ans+((query(yy-1)-query(y)+Mod)%Mod*((C[nl[x]]*C[suml[x]-nl[x]])%Mod))%Mod)%Mod;
}
printf ( "%lld\\n", ans );
return 0;
}
1228: [SDOI2009]E&D
Description
小E 与小W 进行一项名为“E&D”游戏。游戏的规则如下:桌子上有2n 堆石子,编号为1..2n。其中,为了方便起见,我们将第2k-1 堆与第2k 堆(1 ≤ k ≤ n)视为同一组。第i堆的石子个数用一个正整数Si表示。一次分割操作指的是,从桌子上任取一堆石子,将其移走。然后分割它同一组的另一堆石子,从中取出若干个石子放在被移走的位置,组成新的一堆。操作完成后,所有堆的石子数必须保证大于0。显然,被分割的一堆的石子数至少要为2。两个人轮流进行分割操作。如果轮到某人进行操作时,所有堆的石子数均为1,则此时没有石子可以操作,判此人输掉比赛。小E 进行第一次分割。他想知道,是否存在某种策略使得他一定能战胜小W。因此,他求助于小F,也就是你,请你告诉他是否存在必胜策略。例如,假设初始时桌子上有4 堆石子,数量分别为1,2,3,1。小E可以选择移走第1堆,然后将第2堆分割(只能分出1 个石子)。接下来,小W 只能选择移走第4 堆,然后将第3 堆分割为1 和2。最后轮到小E,他只能移走后两堆中数量为1 的一堆,将另一堆分割为1 和1。这样,轮到小W 时,所有堆的数量均为1,则他输掉了比赛。故小E 存在必胜策略。
Input
的第一行是一个正整数T(T ≤ 20),表示测试数据数量。接下来有T组数据。对于每组数据,第一行是一个正整数N,表示桌子上共有N堆石子。其中,输入数据保证N是偶数。第二行有N个正整数S1..SN,分别表示每一堆的石子数。
Output
包含T 行。对于每组数据,如果小E 必胜,则输出一行”YES”,否则输出”NO”。
Sample Input
2
4
1 2 3 1
6
1 1 1 1 1 1
Sample Output
YES
NO
【数据规模和约定】
对于20%的数据,N = 2;
对于另外20%的数据,N ≤ 4,Si ≤ 50;
对于100%的数据,N ≤ 2×104,Si ≤ 2×109。
很明显就是打表sg找规律啦..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
int n;
int sg ( int x, int y ){
LL ret = 2;
for ( LL i = 0; ; i ++, ret <<= 1 ){
if ( x%ret < ret/2 && y%ret < ret/2 ) return i;
}
}
int main (){
int i, j, k, T;
scanf ( "%d", &T );
while ( T -- ){
scanf ( "%d", &n );
int ans = 0;
for ( i = 1; i <= n/2; i ++ ){
int x, y;
scanf ( "%d%d", &x, &y );
ans ^= sg ( x-1, y-1 );
}
if ( ans ) printf ( "YES\\n" );
else printf ( "NO\\n" );
}
return 0;
}
1875: [SDOI2009]HH去散步
Description
HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。 现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径
Input
第一行:五个整数N,M,t,A,B。其中N表示学校里的路口的个数,M表示学校里的 路的条数,t表示HH想要散步的距离,A表示散步的出发点,而B则表示散步的终点。 接下来M行,每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路。数据保证Ai = Bi,但 不保证任意两个路口之间至多只有一条路相连接。 路口编号从0到N − 1。 同一行内所有数据均由一个空格隔开,行首行尾没有多余空格。没有多余空行。 答案模45989。
Output
一行,表示答案。
Sample Input
4 5 3 0 0
0 1
0 2
0 3
2 1
3 2Sample Output
4HINT
对于30%的数据,N ≤ 4,M ≤ 10,t ≤ 10。 对于100%的数据,N ≤ 20,M ≤ 60,t ≤ 2^30,0 ≤ A,B
一看$t$这么大就知道肯定是要套矩阵乘法了..
首先想到的是倍增Floyd.. 但是这题不能走重复边但是能走重边..
然后就是化边为点,先把双向边拆成两条边,然后特殊判断一下这两条边就行了..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 25;
const int Maxm = 130;
const int Mod = 45989;
struct matrix {
int a[Maxm][Maxm];
int l1, l2;
void clear (){ memset ( a, 0, sizeof (a) ); }
}trans, x, z, fi;
matrix ttimes ( matrix x, matrix y ){
matrix ret;
ret.clear ();
ret.l1 = x.l1; ret.l2 = y.l2;
int i, j, k;
for ( i = 0; i < ret.l1; i ++ ){
for ( j = 0; j < ret.l2; j ++ ){
for ( k = 0; k < x.l2; k ++ ){
ret.a[i][j] = (ret.a[i][j]+(x.a[i][k]*y.a[k][j])%Mod)%Mod;
}
}
}
return ret;
}
struct node {
int x, y, next;
}a[Maxm*2]; int first[Maxn], len;
void ins ( int x, int y ){
a[len].x = x; a[len].y = y;
a[len].next = first[x]; first[x] = len;
len ++;
}
int n, m, t, A, B;
int main (){
int i, j, k;
scanf ( "%d%d%d%d%d", &n, &m, &t, &A, &B );
memset ( first, -1, sizeof (first) );
for ( i = 1; i <= m; i ++ ){
int x, y;
scanf ( "%d%d", &x, &y );
ins ( x, y ); ins ( y, x );
}
trans.l1 = trans.l2 = len;
z.l1 = z.l2 = len;
for ( i = 0; i < len; i ++ ){
for ( j = 0; j < len; j ++ ){
if ( (i^1) == j ) continue;
if ( a[i].y == a[j].x ) trans.a[i][j] = 1;
}
z.a[i][i] = 1;
}
x = trans;
for ( i = t-1; i >= 1; i >>= 1 ){
if ( i & 1 ) z = ttimes ( z, x );
x = ttimes ( x, x );
}
fi.l1 = 1; fi.l2 = len;
for ( k = first[A]; k != -1; k = a[k].next ){
fi.a[0][k] = 1;
}
fi = ttimes ( fi, z );
int ans = 0;
for ( k = first[B]; k != -1; k = a[k].next ){
ans = (ans+fi.a[0][k^1])%Mod;
}
printf ( "%d\\n", ans );
return 0;
}
1876: [SDOI2009]SuperGCD
Description
Sheng bill有着惊人的心算能力,甚至能用大脑计算出两个巨大的数的GCD(最大公约 数)!因此他经常和别人比赛计算GCD。有一天Sheng bill很嚣张地找到了你,并要求和你比 赛,但是输给Sheng bill岂不是很丢脸!所以你决定写一个程序来教训他。Input
共两行: 第一行:一个数A。 第二行:一个数B。Output
一行,表示A和B的最大公约数。Sample Input
12
54
Sample Output
6
HINT
对于20%的数据,0 < A , B ≤ 10 ^ 18。
对于100%的数据,0 < A , B ≤ 10 ^ 10000。
高精度gcd啊.. 用更相减损法
取计数变量$g$,初始值为$0$
1)$A\\equiv 0\\pmod{2},B\\equiv 0\\pmod{2}$,累计$g=g+1$,$A$和$B$同时除以$2$
2)$A\\equiv 1\\pmod{2},B\\equiv 0\\pmod{2}$,$B$除以$2$
3)$A\\equiv 0\\pmod{2},B\\equiv 1\\pmod{2}$,$A$除以$2$
4)$A\\equiv 1\\pmod{2},B\\equiv 1\\pmod{2}$,大数减小数..
循环做以上操作知道某个数为$0$
再用另外一个数乘$2^g$就是答案..
听说压位可以让代码跑的更快..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 10010;
struct node {
int a[Maxn], len;
}A, B;
void divi ( node &x, int p ){
int i;
for ( i = x.len; i >= 1; i -- ){
x.a[i-1] += (x.a[i]%p)*1000000000;
x.a[i] /= p;
}
while ( x.a[x.len] == 0 ) x.len --;
}
void minus ( node &x, node &y ){
int i;
for ( i = 1; i <= x.len; i ++ ) x.a[i] -= y.a[i];
for ( i = 1; i <= x.len; i ++ ){
if ( x.a[i] < 0 ){
x.a[i] += 1000000000;
x.a[i+1] --;
}
}
while ( x.a[x.len] == 0 && x.len > 0 ) x.len --;
}
bool bigger (){
int i;
if ( A.len != B.len ) return A.len > B.len;
for ( i = A.len; i >= 1; i -- ){
if ( A.a[i] != B.a[i] ) return A.a[i] > B.a[i];
}
return true;
}
void ttimes ( node &x, int p ){
int i, j;
for ( i = 1; i <= x.len; i ++ ) x.a[i] *= p;
for ( i = 1; i <= x.len; i ++ ){
x.a[i+1] += x.a[i]/1000000000;
x.a[i] %= 1000000000;
}
if ( x.a[x.len+1] > 0 ) x.len ++;
}
char s[Maxn];
int main (){
int i, j, k;
scanf ( "%s", s+1 );
int le = strlen (s+1);
A.len = (le-1)/9+1;
for ( i = 1; i <= A.len; i ++ ){
for ( j = 9; j >= 1; j -- ){
int pos = le-((i-1)*9+j)+1;
if ( pos < 1 ) continue;
A.a[i] = A.a[i]*10+s[pos]-\'0\';
}
}
scanf ( "%s", s+1 );
le = strlen (s+1);
B.len = (le-1)/9+1;
for ( i = 1; i <= B.len; i ++ ){
for ( j = 9; j >= 1; j -- ){
int pos = le-((i-1)*9+j)+1;
if ( pos < 1 ) continue;
B.a[i] = B.a[i]*10+s[pos]-\'0\';
}
}
int g = 0;
while ( A.len > 0 && B.len > 0 ){
if ( A.a[1] % 2 == 0 && B.a[1] % 2 == 0 ){
g ++;
divi ( A, 2 );
divi ( B, 2 );
}
else if ( A.a[1] % 2 == 0 && B.a[1] % 2 != 0 ) divi ( A, 2 );
else if ( A.a[1] % 2 != 0 && B.a[1] % 2 == 0 ) divi ( B, 2 );
else {
if ( bigger () == true ) minus ( A, B );
else minus ( B, A );
}
}
if ( B.len == 0 ){
while ( g -- ) ttimes ( A, 2 );
printf ( "%d", A.a[A.len] );
for ( i = A.len-1; i >= 1; i -- ) printf ( "%09d", A.a[i] );
}
else {
while ( g -- ) ttimes ( B, 2 );
printf ( "%d", B.a[B.len] );
for ( i = B.len-1; i >= 1; i -- ) printf ( "%09d", B.a[i] );
}
printf ( "\\n" );
return 0;
}
1877: [SDOI2009]晨跑
Description
Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑、仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑。 现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街道,Elaxia只能从 一个十字路口跑向另外一个十字路口,街道之间只在十字路口处相交。Elaxia每天从寝室出发 跑到学校,保证寝室编号为1,学校编号为N。 Elaxia的晨跑计划是按周期(包含若干天)进行的,由于他不喜欢走重复的路线,所以 在一个周期内,每天的晨跑路线都不会相交(在十字路口处),寝室和学校不算十字路 口。Elaxia耐力不太好,他希望在一个周期内跑的路程尽量短,但是又希望训练周期包含的天 数尽量长。 除了练空手道,Elaxia其他时间都花在了学习和找MM上面,所有他想请你帮忙为他设计 一套满足他要求的晨跑计划。Input
第一行:两个数N,M。表示十字路口数和街道数。 接下来M行,每行3个数a,b,c,表示路口a和路口b之间有条长度为c的街道(单向)。Output
两个数,第一个数为最长周期的天数,第二个数为满足最长天数的条件下最短的路程长 度。Sample Input
7 10
1 2 1
1 3 1
2 4 1
3 4 1
4 5 1
4 6 1
2 5 5
3 6 6
5 7 1
6 7 1Sample Output
2 11HINT
对于30%的数据,N ≤ 20,M ≤ 120。
对于100%的数据,N ≤ 200,M ≤ 20000。
这道题有多无聊呢.. 无聊到就是一个拆点裸的费用流..
他的题目要求完全就是赤裸裸的告诉你这是最小费用最大流
不信吗 最长周期->最大流 最短路程->最小费用..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
using namespace std;
const int Maxn = 210;
const int Maxm = 21000;
struct node {
int x, y, next, c, d, opp;
}a[Maxm*10]; int first[Maxn*2], len;
void ins ( int x, int y, int c, int d ){
len ++; int k1 = len;
a[len].x = x; a[len].y = y; a[len].c = c; a[len].d = d;
a[len].next = first[x]; first[x] = len;
len ++; int k2 = len;
a[len].x = y; a[len].y = x; a[len].c = 0; a[len].d = -d;
a[len].next = first[y]; first[y] = len;
a[k1].opp = k2;
a[k2].opp = k1;
}
int n, m;
int st, ed;
int pre[Maxn*2], d[Maxn*2];
bool v[Maxn*2];
bool spfa (){
queue <int> q;
memset ( d, 63, sizeof (d) );
memset ( v, false, sizeof (v) );
memset ( pre, -1, sizeof (pre) );
q.push (st); d[st] = 0; v[st] = true;
while ( !q.empty () ){
int x = q.front (); q.pop ();
for ( int k = first[x]; k; k = a[k].next ){
int y = a[k].y;
if ( d[y] > d[x]+a[k].d && a[k].c > 0 ){
d[y] = d[x]+a[k].d;
pre[y] = k;
if ( v[y] == false ){
v[y] = true;
q.push (y);
}
}
}
v[x] = false;
}
return pre[ed] > 0;
}
int main (){
int i, j, k, x, y;
scanf ( "%d%d", &n, &m );
st = 1+n; ed = n;
for ( i = 1; i <= m; i ++ ){
scanf ( "%d%d%d", &x, &y, &k );
ins ( x+n, y, 1, k );
}
for ( i = 1; i <= n; i ++ ) ins ( i, i+n, 1, 0 );
int ans = 0, sum = 0;
while ( spfa () ){
ans += d[ed];
sum ++;
for ( i = ed; i != st; i = a[pre[i]].x ){
a[pre[i]].c --;
a[a[pre[i]].opp].c ++;
}
}
printf ( "%d %d\\n", sum, ans );
return 0;
}
1878: [SDOI2009]HH的项链
Description
HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此, 他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同 的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只好求助睿智的你,来解 决这个问题。Input
第一行:一个整数N,表示项链的长度。 第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。 第三行:一个整数M,表示HH询问的个数。 接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。Output
M行,每行一个整数,依次表示询问对应的答案。Sample Input
6
1 2 3 4 3 5
3
1 2
3 5
2 6
Sample Output
2
2
4
HINT
对于20%的数据,N ≤ 100,M ≤ 1000;
对于40%的数据,N ≤ 3000,M ≤ 200000;
对于100%的数据,N ≤ 50000,M ≤ 200000。
涨姿势了.. 学到一发压常数的方法.. 以后有机会出题卡死你们..
这题很明显就是离线做,询问按左端点排个序,记录每个点颜色下一次出现的位置,就能知道当左端点从$i$移动到$i+1$时影响的区间了..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 50010;
const int Maxm = 200010;
const int Maxnum = 1000010;
struct node {
int l, r, num;
}q[Maxm]; int ans[Maxm];
bool cmp ( node x, node y ){ return x.l < y.l; }
int sum[Maxn], nxt[Maxn], last[Maxnum];
int n, m;
int lowbit ( int x ){ return x & (-x); }
void add ( int x ){
while ( x <= n ){
sum[x] ++;
x += lowbit (x);
}
}
int query ( int x ){
int ret = 0;
while ( x > 0 ){
ret += sum[x];
x -= lowbit (x);
}
return ret;
}
int main (){
int i, j, k;
scanf ( "%d", &n );
for ( i = 1; i <= n; i ++ ){
int x;
scanf ( "%d", &x );
if ( last[x] == 0 ) add (i);
else nxt[last[x]] = i;
last[x] = i;
}
scanf ( "%d", &m );
for ( i = 1; i <= m; i ++ ) scanf ( "%d%d", &q[i].l, &q[i].r ), q[i].num = i;
sort ( q+1, q+m+1, cmp );
j = 1;
for ( i = 1; i <= n; i ++ ){
while ( q[j].l == i ){
ans[q[j].num] = query (q[j].r) - query (q[j].l-1);
j ++;
}
if ( nxt[i] != 0 ) add (nxt[i]);
}
for ( i = 1; i <= m; i ++ ) printf ( "%d\\n", ans[i] );
return 0;
}
1879: [Sdoi2009]Bill的挑战
Description
Input
本题包含多组数据。 第一行:一个整数T,表示数据的个数。 对于每组数据: 第一行:两个整数,N和K(含义如题目表述)。 接下来N行:每行一个字符串。Output
1 2 1 a? ?bSample Input
50Sample Output
对于30%的数据,T ≤ 5,M ≤ 5,字符串长度≤ 20;
对于70%的数据,T ≤ 5,M ≤ 13,字符串长度≤ 30;
对于100%的数据,T ≤ 5,M ≤ 15,字符串长度≤ 50。
看这数据范围这么小,仔细想想就知道肯定又是一道状压dp了..
$f_{i,j}$表示到第$i$位,能匹配的串状态为$j$的方案数..
枚举$26$个字母就可以了..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 55;
const int Mod = 1e6+3;
char s[20][Maxn];
int f[Maxn][(1<<16)];
int n, K, len;
int d[20];
int main (){
int i, j, k, T;
scanf ( "%d", &T );
d[1] = 1;
for ( i = 2; i <= 17; i ++ ) d[i] = d[i-1]<<1;
while ( T -- ){
scanf ( "%d%d", &n, &K );
for ( i = 1; i <= n; i ++ ) scanf ( "%s", s[i]+1 );
len = strlen (s[1]+1);
memset ( f, 0, sizeof (f) );
f[0][d[n+1]-1] = 1;
for ( i = 0; i < len; i ++ ){
for ( j = 0; j < d[n+1]; j ++ ){
if ( f[i][j] == 0 ) continue;
for ( k = 0; k < 26; k ++ ){
int p = j;
for ( int x = 1; x <= n; x ++ ){
if ( j & d[x] ){
if ( s[x][i+1] != k+\'a\' && s[x][i+1] != \'?\' ) p -= d[x];
}
}
f[i+1][p] = (f[i+1][p]+f[i][j])%Mod;
}
}
}
int ans = 0;
for ( i = 0; i < d[n+1]; i ++ ){
int ss = 0;
for ( j = 1; j <= n; j ++ ) if ( i & d[j] ) ss ++;
if ( ss != K ) continue;
ans = (ans+f[len][i])%Mod;
}
printf ( "%d\\n", ans );
}
return 0;
}
1880: [Sdoi2009]Elaxia的路线
Description
最近,Elaxia和w**的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们 必须合理地安排两个人在一起的时间。Elaxia和w**每天都要奔波于宿舍和实验室之间,他们 希望在节约时间的前提下,一起走的时间尽可能的长。 现在已知的是Elaxia和w**所在的宿舍和实验室的编号以及学校的地图:地图上有N个路 口,M条路,经过每条路都需要一定的时间。 具体地说,就是要求无向图中,两对点间最短路的最长公共路径。Input
第一行:两个整数N和M(含义如题目描述)。 第二行:四个整数x1、y1、x2、y2(1 ≤ x1 ≤ N,1 ≤ y1 ≤ N,1 ≤ x2 ≤ N,1 ≤ ≤ N),分别表示Elaxia的宿舍和实验室及w**的宿舍和实验室的标号(两对点分别 x1,y1和x2,y2)。 接下来M行:每行三个整数,u、v、l(1 ≤ u ≤ N,1 ≤ v ≤ N,1 ≤ l ≤ 10000),表 u和v之间有一条路,经过这条路所需要的时间为l。 出出出格格格式式式::: 一行,一个整数,表示每天两人在一起的时间(即最长公共路径的长度)。Output
一行,一个整数,表示每天两人在一起的时间(即最长公共路径的长度)Sample Input
9 10
1 6 7 8
1 2 1
2 5 2
2 3 3
3 4 2
3 9 5
4 5 3
4 6 4
4 7 2
5 8 1
7 9 1
Sample Output
3HINT
对于30%的数据,N ≤ 100;
对于60%的数据,N ≤ 1000;
对于100%的数据,N ≤ 1500,输入数据保证没有重边和自环。
又被la1la1la大神d了.. 他说这题很水.. 仔细想想的确很水..
spfa求出四个点到各个点的最短路,然后枚举两个点作为最长路径的两个端点再判断即可..
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <queue> using namespace std; const int Maxn = 1510; const int inf = 1e9; struct node { int y, next, d; }a[Maxn*Maxn]; int first[Maxn], len; void ins ( int x, int y, int d ){ len ++; a[len].y = y; a[len].d = d; a[len].next = first[x]; first[x] = len; } int n, m; int st1, ed1, st2, ed2; int dist[2][2][Maxn]; bool v[Maxn]; void spfa ( int *dis, int start ){ queue <int> q; for ( int i = 1; i <= n; i ++ ) dis[i] = inf; q.push (start); dis[start] = 0; v[start] = true; while ( !q.empty () ){ int x = q.front (); q.pop (); for ( int k = first[x]; k; k = a[k].next ){ int y = a[k].y; if ( dis[y] > dis[x]+a[k].d ){ dis[y] = dis[x]+a[k].d; if ( v[y] == false ){ v[y] = true; q.push (y); } } } v[x] = false; } } int _max ( int x, int y ){ return x > y ? x : y; } int main (){ int i, j, k; scanf ( "%d%d", &n, &m ); scanf ( "%d%d%d%d", &st1, &ed1, &st2, &ed2 ); for ( i = 1; i <= m; i ++ ){ int x, y; scanf ( "%d%d%d", &x, &y, &k ); ins ( x, y, k ); ins ( y, x, k ); } spfa ( dist[0][0], st1 ); spfa ( dist[0][1], ed1 ); spfa ( dist[1][0], st2 ); spfa ( dist[1][1], ed2 ); int ans = 0; for ( i = 1; i <= n; i ++ ){ if ( dist[0][0][i]+dist[0][1][i] > dist[0][0][ed1] ) continue; if ( dist[1][0][i]+dist[1][1][i] > dist[1][0][ed2] ) continue; for ( j = 1; j <= n; j ++ ){ if ( dist[0][0][j]+dist[0][1][j] > dist[0][0][ed1] ) continue; if ( dist[1][0][j]+dist[1][1][j] > dist[1][0][ed2] ) continue; ans = _max ( ans, dist[0][0][j]-dist[0][0][i] ); } } printf ( "%d\\n", ans ); return 0; }
写个总结什么的..
讲道理这套题还是很水的,让我开心了几天..
然后其实做水题并没有什么卵用啊..
但是在做这套题的时候还是发现了有很多没想到的地方
比如有些应该一眼的状压dp、通过压位的方法来卡高精度时间..
嗯以后还是应该多想想的..
以上是关于SDOI2009的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ1878: [SDOI2009]HH的项链 (主席树)