【题目大意】
给定N个串,再给定一个主串,问将主串改为不含这个N个串为子串的串,至少需改动多少个字母?
【算法分析】
嗯,我又在刷水题。。。
就是建一个AC自动机(DFA)。
然后用F[i,j]表示已处理串长为i,然后在自动机中的状态j,至少要改动多少个字母。
转移就是用自动机啦。
【其它】
1Y
注意如果自动机中的next结点有终结标记,自己也应当标为终止。
【CODE】
#include
【题目大意】
给定N个串,再给定一个主串,问将主串改为不含这个N个串为子串的串,至少需改动多少个字母?
【算法分析】
嗯,我又在刷水题。。。
就是建一个AC自动机(DFA)。
然后用F[i,j]表示已处理串长为i,然后在自动机中的状态j,至少要改动多少个字母。
转移就是用自动机啦。
【其它】
1Y
注意如果自动机中的next结点有终结标记,自己也应当标为终止。
【CODE】
#include
【题目链接】http://ace.delos.com/ioiupload?init=1&a=uNRSh9JKRlk
【题目大意】
给定n<=300个形如ax+by+cz>=0的不等式。(将<=0的乘一个 -1来改变不等号方向)
求在其满足的前提下,给出m个询问,
看能不能确定另外给出的式子ax+by+cz是>0还是<0还是无法确定。
它很猥琐的还有常量条件。
x>0
y>0
z>0
x<=100y
x<=100z
y<=100x
y<=100z
z<=100x
z<=100y
就这么多。
【算法分析】
由于x,y,z>0,所以我们将不等式两边同除以z,不用变号。
那么不等式变成:a(x/z)+b(x/z)+c>=0
我们令X=x/z,Y=x/z。
那么不等式变为aX+bY+c>=0。
然后?
半平面交!
就是先对给出的条件全部变为这种限制,然后求出其半平面交。
然后这个半平面交的算法是我自己YY出来的,O(n^2)。。。小有成就感。
当然,众神牛估计要笑而不语了。
好了,半平面交完以后呢,就只要对每个给出的限制转化为直线。然后看这个直线是“穿过”这个半平面交。
如果穿过,表示两边的取值都可能,所以不能确定他的不等号方向。
否则,看就看它在哪一边就行了。
【其它】
好有好多好多小细节。。。然后我写成的程序,精度要卡得刚好才能过= =
如果要学习半平面交的话。。。还是别看我这。因为写得实在太水,太长了。
我看过师傅的模板,不禁为其短小精悍而叹服。
但是毕竟是自己推得算法。。。再水也是自己的= =
【CODE】
/*
ID:jie22221
TASK:starc
LANG:C++
*/
#include
#define INF 100
#define eps 1e-6
#define seps 1e-2
using namespace std;
struct Line{double a,b,c;}LL[N];
struct Point{double x,y;}p[N*N],tp[N*N];
int n,Ln,m,tn;
void init(){
int i,A1,B1,C1,A2,B2,C2;
char ch;
cin >> Ln >> m;
for (i=1;i<=Ln;i++){
cin >> ch >> A1 >> B1 >> C1 >> A2 >> B2 >> C2;
if (ch==’J’){
LL[i].a=A1-A2;
LL[i].b=B1-B2;
LL[i].c=C1-C2;
}else{
LL[i].a=A2-A1;
LL[i].b=B2-B1;
LL[i].c=C2-C1;
}
}
Ln++;
LL[Ln].a=-1;
LL[Ln].b=100;
LL[Ln].c=0;
Ln++;
LL[Ln].a=100;
LL[Ln].b=-1;
LL[Ln].c=0;
n=4;
p[1].x=seps; p[1].y=seps;
p[2].x=INF; p[2].y=seps;
p[3].x=INF; p[3].y=INF;
p[4].x=seps; p[4].y=INF;
}
inline double f(Line L,Point pp){
return L.a*pp.x+L.b*pp.y+L.c;
}
inline double cj(Point A,Point B){return A.x*B.y-A.y*B.x;}
inline void dec(int &x){x–; if (x<1) x=n;}
inline void inc(int &x){x++; if (x>n) x=1;}
Line Get_Line(Point A,Point B){
Line res;
res.a=A.y-B.y;
res.b=B.x-A.x;
res.c=cj(A,B);
return res;
}
Point Cross_Point(Line A,Line B){
Point res;
res.x=(B.b*A.c-A.b*B.c)/(B.a*A.b-A.a*B.b);
res.y=(B.a*A.c-A.a*B.c)/(A.a*B.b-B.a*A.b);
return res;
}
double fabs(double x){
if (x<0) return -x;
return x;
}
void Cut(Line L){
int i,j,k,st=1;
bool flag=false;
for (i=1;i<=n;i++)
if (f(L,p[i])<-eps) flag=true;
if (!flag){
for (i=1;i<=n;i++) tp[i]=p[i];
tn=n;
return;
}
while (f(L,p[st])<-eps) st++;
for (i=st;;inc(i))
if (f(L,p[i])<-eps) break;
for (j=st;;dec(j))
if (f(L,p[j])<-eps) break;
tn=0;
for (k=j%n+1;k!=i;inc(k))
tp[++tn]=p[k];
dec(i);
if (fabs(f(L,tp[tn]))>eps){
Line tmp=Get_Line(p[i],p[i%n+1]);
tp[++tn]=Cross_Point(tmp,L);
}
if (fabs(f(L,tp[ 1]))>eps){
Line tmp=Get_Line(p[j],p[j%n+1]);
tp[++tn]=Cross_Point(tmp,L);
}
}
void half_plane(){
int i,j;
for (i=1;i<=Ln;i++){
Cut(LL[i]);
for (j=1;j<=tn;j++) p[j]=tp[j];
n=tn;
}
}
void solve(){
int A1,B1,C1,A2,B2,C2,t1,t2,i,j;
for (i=1;i<=m;i++){
t1=t2=0;
cin >> A1 >> B1 >> C1 >> A2 >> B2 >> C2;
Line L;
L.a=A1-A2;
L.b=B1-B2;
L.c=C1-C2;
for (j=1;j<=n;j++){
if (f(L,p[j])<-1e-8) t1++;
if (f(L,p[j])> 1e-8) t2++;
}
if (t1==0 && t2==0 || t1+t2!=n) puts("U"); else
if (!t1) puts("J"); else
if (!t2) puts("B"); else
puts("U");
}
}
int main(){
freopen("starc.in","r",stdin);
freopen("starc.out","w",stdout);
ios::sync_with_stdio(false);
init();
half_plane();
solve();
return 0;
}
【题目链接】
http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1777
http://ace.delos.com/ioiupload?d=gold&a=nejLCFTaRn6&lang=ch
【Experience】
呃,今天弄了一下那个论文,以及参详了DD——崔牛的神文的一部分。
神文地址:http://hi.baidu.com/yufuwan1/blog/item/4bb477443c425d4a500ffe2d.html
此乃转载地址,好像原地址已经消失不见了。。。
于是论文自然就是WC2009的:《从“k倍动态减法游戏”出发探究一类组合游戏问题》
然后表示,NIM积依然不懂,第一个例子就不懂。。。
幸好这题不用NIM积。。。
【算法分析】
首先我们可以发现,如果是离根距离为偶数的点上面的石头是不可能决定胜负的。
因为,假设一个人把一些石头从偶点移动一次,那么就到达一个奇点,然后另外一个人就能再从这个奇点移同样数目的石头到偶点。直到这些石头到根。
如果按照这个策略做的话,结果不会发现任何改变。
所以偶点的石头与胜负无关。
然后接下来我们来看奇点的石头。
假设我们从这个奇点移了一些石头,那么再偶数点上的石头再次对结果无关。
所以,每个奇点上的石头是独立的。
有了独立这个性质,那么我们就可以利用Sprague-Grundy Theorem
来处理。如果你看了上面崔牛那篇的第二部分,那么就很容易想到了。
因为是独立的游戏,那么SG(X)=SG(a1)^SG(a2)^…^SG(an)。(^即xor)
如果SG(X)=0,那么证明X是先手必败态。
然后每次只能移L这么多个石头,有一种很简单的方法得出每一个奇点的SG函数值。
就是SG=Rocks_Num%(L+1)。
如果你看了DD那篇神文。
就容易知道SG是怎么求出来的了。
就是画一个有向图,每个点代表剩下x个石子时候的局面,然后再连转移的有向边,然后。。。就能发现规律了。
于是根据Sprague-Grundy Theorem,我们可以轻易得到整个局面的SG函数。
然后剩下的问题是关心在它变那些值的时候我们怎么处理。
由于我们实际上就是需要维护整个局面的SG值,于是我们可以围观到这样的式子:
Ans=k ^ Ai
Ans’=k ^ Ai‘
于是Ans’=k ^ Ai ^ Ai ^ Ai’=Ans ^ Ai ^ Ai’
嗯,就这样。
【CODE】
/*
ID:jie22221
TASK:rocks
LANG:C++
*/
#include
int d[10005],SG[10005];
int main(){
freopen("rocks.in","r",stdin);
freopen("rocks.out","w",stdout);
scanf("%d%d%d",&N,&T,&L);
d[1]=0; SG[1]=ans=0;
for (i=2;i<=N;i++){
scanf("%d%d",&fa,&w);
d[i]=d[fa]+1;
SG[i]=w%(L+1);
if (d[i]%2==1) ans^=SG[i];
}
for (i=1;i<=T;i++){
scanf("%d%d",&x,&w);
if (d[x]%2==1){
w%=(L+1);
ans^=SG[x]^w;
SG[x]=w;
}
if (ans==0) puts("No");
else puts("Yes");
}
}
【题目】
提交文件:road.pas/.cpp
输入文件:road.in
输出文件:road.out
我们知道,每天都有成千上万的车辆在高速公路上行驶。如果一辆装有若干吨货物的卡车通过一段高速公路,就会对这段公路造成一定程度的破坏。
整段高速公路有一个初始的耐久度I。如果一辆装有d吨货物的卡车通过一段高速公路,这段公路的耐久度就会减少d。一旦某段公路的耐久度小于或等于0,这段公路就会永久性地毁坏。卡车无法通过已经毁坏的地方。
有两种维护公路的车辆:T1和T2。T1车可以将一段公路的耐久度增加r。T2车可以将一段公路的耐久度小于p的部分修复至p。虽然维护公路的车辆可以通过已经毁坏的部分,但是已经毁坏的地方仍然无法修复。
你的任务是统计一下一共有多少辆卡车可以成功通行。
输入格式:
第一行是三个整数N、M和I,表示高速公路的长度为N,有M辆车依次通过,整段高速公路的初始的耐久度为I。
以下M行每行描述一辆车,格式如下:
1 s t d:一辆装有d吨货物的卡车准备通过区间[s,t]。在此之前先检查区间[s,t]是否完好。如果区间[s,t]里面有任何一个地方毁坏了,这辆卡车就会取消整个通行计划;否则这辆卡车就可以成功通过区间[s,t],即使通过之后会把某些地方毁坏。
2 s t r:一辆T1车通过区间[s,t],为这段公路的耐久度增加r。
3 s t p:一辆T2车通过区间[s,t],为这段公路的耐久度修复至p。
其中:1≤N≤100000,1≤M≤100000,1≤I≤1000,1≤s≤t≤N,1≤d≤1000,1≤r≤1000,1≤p≤1000。
输出格式:
输出一个数,表示有多少辆卡车可以成功通行。
输入样例:
3 5 2
2 1 3 3
3 1 3 4
1 1 2 3
1 2 3 2
1 1 3 1
输出样例:
2
样例解释:
第3辆卡车无法通过区间[1,3]。因为虽然区间[1,2)和区间(2,3]的耐久度大于0,但是2这个点的耐久度为0,所以卡车无法通过。
【算法分析】
这个题就牛X了。。。至少对我来说。
块状链表嘛,就是把长度为n的整个数列分成sqrt(n)份,那么每份就有sqrt(n)个元素了。
然后维护三个标记。
1、key,表示这个块里最小的一个数是多少。
2、delta,这个块里的整体增值。
3、cc,这个块 < cc的要先变成cc。
4、low,这个块上一次标记清空之后,如果a[i]<=low,那么这个路就爆了。
然后我们设定cc都是在delta之前。那么维护上就有一点技巧了。
如果想不通的话就看程序吧。。。我觉得还是写得挺清晰地
= =
然后线段树的话,对于“重叠的标记”和“重叠标记”之间的合并无法做到。
于是无法完成这个任务。
【CODE】
#include
#define max(x,y) ((x)>(y)?(x):(y))
const int N=100005;
struct Node{int l,r,key,delta,low,cc;}p[N/100];
int a[N],L;
int n,m,Stw,ans;
void build(){
for (int i=1;i<=n;i++) a[i]=Stw;
L=0;
int part=(int)(sqrt(n));
if (part==0) part=1;
for (int i=1;(i-1)*part+1<=n;i++){
L=i;
p[i].l=(i-1)*part+1;
p[i].r=min(i*part,n);
p[i].key=Stw;
p[i].delta=p[i].low=p[i].cc=0;
}
}
void push(int i){
for (int j=p[i].l;j<=p[i].r;j++){
if (a[j]<=p[i].low){
a[j]=0;
continue;
}
a[j]>?=p[i].cc;
a[j]+=p[i].delta;
}
}
void remake(int i){
p[i].delta=p[i].low=p[i].cc=0;
p[i].key=0x7FFFFFFF;
for (int j=p[i].l;j<=p[i].r;j++)
p[i].key=a[j];
}
void Deal1(int l,int r,int w){
int i,j;
for (i=1;i<=L;i++){
if (r
void Deal2(int l,int r,int w){
int i,j;
for (i=1;i<=L;i++){
if (r
void Deal3(int l,int r,int w){
int i,j;
for (i=1;i<=L;i++){
if (r
void solve(){
int i,op,l,r,w;
scanf("%d%d%d",&n,&m,&Stw);
build();
ans=0;
for (i=1;i<=m;i++){
scanf("%d%d%d%d",&op,&l,&r,&w);
switch (op){
case 1:
Deal1(l,r,w);
break;
case 2:
Deal2(l,r,w);
break;
case 3:
Deal3(l,r,w);
break;
}
}
printf("%dn",ans);
}
int main(){
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
solve();
}
【题目】
提交文件:pick.pas/.cpp
输入文件:pick.in
输出文件:pick.out
Feather的农场里有N*M块地,排列成N行,每行M块地。Feather在每块地里种植了不同的农作物。现在这些农作物都成熟了,可以摘取下来出售了。其中第i行第j列的地里的农作物的价值为W[i,j]。
JackRabbit是Feather的好友,平时经常为Feather的农作物除草除虫。为了答谢JackRabbit,Feather决定把一部分农作物送给JackRabbit。JackRabbit很高兴,恨不得一下子把农场里的农作物摘空。
为了防止JackRabbit把农作物摘空,Feather提出了两个条件:
1.每行最多选取两块地;
2.每列最多选取两块地。
这下子把JackRabbit难住了。如何在满足这两个条件的前提下,使得摘取的农作物的价值之和最大呢?
输入格式:
第一行是两个整数N和M(3≤N≤30,3≤M≤30)。
以下N行每行M个整数,表示每块地的农作物的价值W[i,j](0≤W[i,j]≤10000)。
输出格式:
输出一个数,表示在满足条件的前提下摘取的农作物的价值之和的最大值。
输入样例:
3 3
1 5 3
4 7 5
0 4 1
输出样例:
21
样例解释:
第一行选5和3,第二行选4和5,第三行选0和4,总和为21,是满足条件的最佳选取方案。
【算法分析】
本题是这套题里最脑残的题目。
就是二分图。左边的点是行,右边的点是列。
中间连容量为1,费用为a[i][j]的边。
左右两边与源汇连容量为2的边。
但是直接这样求最大费用最大流是会错误的。
为什么呢?
因为最大流不一定取得最大费用。
最大费用最大流是保证流量最大的情况下取得的费用最大。
于是我们需要将二分图左边的点都向汇连容量为2的边。这要可以允许“不选满”。
然后最后的流量必定是2*n (n是二分图左边的点数)。
又能保证费用最大。
【其它】
在交卷之前,我对我可爱的徒弟小肥闽曰:“你第三题不要错了啊~”
肥闽答曰:“第三题错了我自切JJ!”
然后,他果断错了1个点,就是因为没有处理上述那种情况,于是果断SB。要是数据再黑一点果断要挂。。。。
【CODE】
#include
const int INF=1000000000;
struct edge{int x,y,c,w;edge *next,*op;}*ls[N],*fa[N];
int m,n,vs,vt,cost;
int d[N],Q[N],v[N];
void addedge(int x,int y,int c,int w){
edge *p=(edge*)malloc(sizeof(edge));
edge *q=(edge*)malloc(sizeof(edge));
p->x=x; p->y=y; p->c=c; p->w= w; p->next=ls[x]; ls[x]=p; p->op=q;
q->x=y; q->y=x; q->c=0; q->w=-w; q->next=ls[y]; ls[y]=q; q->op=p;
}
void init(){
int i,j,x;
scanf("%d%d",&m,&n);
for (i=1;i<=m;i++)
for (j=1;j<=n;j++){
scanf("%d",&x);
addedge(i,m+j,1,x);
}
vs=0; vt=m+n+1;
for (i=1;i<=m;i++){
addedge(vs,i,2,0);
addedge(i,vt,2,0);
}
for (i=1;i<=n;i++) addedge(m+i,vt,2,0);
}
bool spfa(){
int head=0,tail=1;
for (int i=vs;i<=vt;i++){
d[i]=-INF;
v[i]=0;
}
d[vs]=0; v[vs]=1; Q[1]=vs;
while (head!=tail){
head++; if (head>=N) head=0;
for (edge *t=ls[Q[head]];t;t=t->next)
if (t->c && d[t->x]+t->w>d[t->y]){
d[t->y]=d[t->x]+t->w;
fa[t->y]=t;
if (!v[t->y]){
v[t->y]=1;
tail++; if (tail>=N) tail=0;
Q[tail]=t->y;
}
}
v[Q[head]]=0;
}
if (d[vt]<=0) return false;
return true;
}
void change(){
int nf=INF;
for (int i=vt;i!=vs;i=fa[i]->x)
if (fa[i]->c
for (int i=vt;i!=vs;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}
}
void MCMF(){
cost=0;
while (spfa()) change();
printf("%dn",cost);
}
int main(){
freopen("pick.in","r",stdin);
freopen("pick.out","w",stdout);
memset(ls,0,sizeof(ls));
init();
MCMF();
}