[POI2009]石子游戏Kam 【博弈】【分组】★★

【题目地址】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1115
【算法分析】
先假设n为偶数,然后对于任意偶数i<=n,都满足a[i]=a[i-1]。
那么显然先手只能取奇数位置a[j]的石头。然后后手必然可以取同样数量的a[j+1]。那么最后必然是后手的取完石头。所以先手必败。

对于n为偶数时的一般情况,依然是连续的每两个分组。
我们可以将这个模型分成两部分,一部分是a[i-1]=a[i]的,另一部分是a[i]-a[i-1]。(i为偶数)
然后一部分用前面那个解决,后者,将两堆合成一个整体看,就变成一般的NIM游戏了!
于是利用SG函数那个xor定理就可以解决了。。
【CODE】
#include int Tc,n,i,ans,a[1005];
int main(){
scanf("%d",&Tc);
for (;Tc;Tc–){
scanf("%d",&n);
for (i=0;i if (n%2) ans=a[0]; else ans=0;
for (i=n-1;i>0;i-=2) ans^=(a[i]-a[i-1]);
if (ans) puts("TAK");
else puts("NIE");
}
}

[JSOI2008]星球大战starwar 【并差集】【逆序处理】

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1015
【算法分析】
就是逆过来,把拆分变成合并,用并差集即可。
【其它】= =1RE。n<=2m我还以为是他搞错了压在一起。。。
【CODE】
#include #include #include using namespace std;
const int N=405555;
struct edge{int y;edge *next;}g[405555],*ls[N];
int n,m,ans,e,Qn;
int F[N],Num[N],Q[N],v[N],reply[N];

inline void addedge(int x,int y){
g[++e].y=y; g[e].next=ls[x]; ls[x]=&g[e];
}

int GF(int k){
int res,i,tmp;
for (i=k;F[i]!=i;i=F[i]);
res=i;
for (i=k;F[i]!=i;tmp=F[i],F[i]=res,i=tmp);
return res;
}

int main(){
e=0;
memset(ls,0,sizeof(ls));
memset(v,0,sizeof(v));
int x,y,i;
scanf("%d%d",&n,&m);
for (i=0;i scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
for (i=0;i F[i]=i;
Num[i]=1;
}
scanf("%d",&Qn);
ans=n;
for (i=0;i scanf("%d",&Q[i]);
if (!v[Q[i]]) ans–;
v[Q[i]]++;
}
for (i=0;i for (edge *t=ls[i];t;t=t->next)
if (!v[t->y] && GF(i)!=GF(t->y)){
ans–;
x=F[i]; y=F[t->y];
if (Num[x]>Num[y]) swap(x,y);
F[x]=y;
Num[y]+=Num[x];
}
reply[Qn]=ans;
for (i=Qn-1;i>=0;i–){
v[Q[i]]–;
if (!v[Q[i]]){
ans++;
for (edge *t=ls[Q[i]];t;t=t->next)
if (!v[t->y] && GF(Q[i])!=GF(t->y)){
ans–;
x=F[Q[i]]; y=F[t->y];
if (Num[x]>Num[y]) swap(x,y);
F[x]=y;
Num[y]+=Num[x];
}
}
reply[i]=ans;
}
for (int i=0;i<=Qn;i++) printf("%dn",reply[i]);
}

[JSOI2008]球形空间产生器sphere 【高斯消元】

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1013
【题目大意】
求高维球心坐标。
【算法分析】
首先距离公式容易推知,然后点到球心距离必然都相等,于是联立n条等式,高斯消元即可。
另外:
我爬山法好像陷进局部最优解里去了,我爬山用的是方差作估价函数,直接WA。
估计最后一名的800+MS的神牛就是爬山过的。求爬山正确的估价函数。
【其它】高斯消元1Y。
【CODE】
#include #include #include #include #define sqr(x) ((x)*(x))
const int N=15;
int n;
double a[N][N],c[N][N];

void Swap(int x,int y){
double tmp;
for (int i=0;i<=n;i++){
tmp=c[x][i];
c[x][i]=c[y][i];
c[y][i]=tmp;
}
}

void guass(){
int i,j,k;
for (i=1;i<=n;i++){
for (k=i,j=i;j<=n;j++)
if (fabs(c[j][i-1])>fabs(c[k][i-1])) k=j;
Swap(i,k);
for (j=i+1;j<=n;j++)
for (k=n;k>=i-1;k–)
c[j][k]-=c[i][k]*c[j][i-1]/c[i][i-1];
}
}

void solve(){
double ans[N];
memset(ans,0,sizeof(ans));
ans[n]=1;
for (int i=n;i>=1;i–){
double Sum=0;
for (int j=i;j<=n;j++)
Sum-=ans[j]*c[i][j];
ans[i-1]=Sum/c[i][i-1];
}
for (int i=0;i if (i) printf(" ");
printf("%.3lf",ans[i]);
}
printf("n");
}

int main(){
scanf("%d",&n);
for (int i=0;i<=n;i++)
for (int j=0;j scanf("%lf",&a[i][j]);
memset(c,0,sizeof(c));
for (int i=1;i<=n;i++){
for (int j=0;j c[i][j]=2*(a[i][j]-a[i-1][j]);
c[i][n]+=sqr(a[i-1][j])-sqr(a[i][j]);
}
}
guass();
solve();
}

[JSOI2008]最大数maxnumber 【线段树】

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1012
【算法分析】
线段树,会的人都知道。。。
【其它】1Y。用cin果断跑到几乎最慢
【做题原因】邻近省选,练练手,刷出1Y,刷出自信。
【CODE】
#include #include #include #include using namespace std;
const int INF=0x7FFFFFFF;
struct Node{int l,r,key;}Tr[1000000];
int m,mod,n,ans;

void build(int p,int l,int r){
Tr[p].l=l; Tr[p].r=r; Tr[p].key=-INF;
if (l int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
}

void Add(int p,int t,int key){
if (Tr[p].l==Tr[p].r){
Tr[p].key=key;
return;
}
if (t<=(Tr[p].l+Tr[p].r)/2) Add(p*2,t,key);
else Add(p*2+1,t,key);
Tr[p].key=max(Tr[p*2].key,Tr[p*2+1].key);
}

void Get(int p,int l,int r){
if (l<=Tr[p].l && Tr[p].r<=r){
ans=max(ans,Tr[p].key);
return;
}
int mid=(Tr[p].l+Tr[p].r)/2;
if (l<=mid) Get(p*2,l,r);
if (r> mid) Get(p*2+1,l,r);
}

int main(){
ios::sync_with_stdio(false);
ans=n=0;
cin >> m >> mod;
build(1,1,m);
for (int i=1;i<=m;i++){
char ch; __int64 x;
cin >> ch >> x;
if (ch==’A’){
x=(x+ans)%mod;
n++;
Add(1,n,(int)(x));
}
else{
ans=-INF;
Get(1,n-x+1,n);
cout << ans << "n";
}
}
}

[HDOJ3335 Divisibility]【DAG图的最大独立集】【最大反链】【最小链划分】【最小路径覆盖】

【题目大意】
给定N个数,让你取最多的数,使得取出的数里,不存在A是B的倍数这种情况。
【算法分析】
虽然当时ACM_DIY群赛时蒙对了算法,当时推理方式见(那叫一个强力):
http://hi.baidu.com/edwardmj/blog/item/1ae99b3f5f5cbee53d6d9789.html
但一直没有理解,于是今天重温二分图时回来做了一下。
本题可以先把相同的数去掉,然后若A为B的倍数,那么B->A。
由于A>B,那么是一个DAG图(有向无环图)
然后DAG图的最大反链就可以利用匹配来搞。(传递闭包后的DAG图的最大独立集=最大反链)
具体做法是每个点拆两个点,弄成二分图,然后匹配。最后输出n-匹配数。
二分图总结见:http://hi.baidu.com/edwardmj/blog/item/b5fc0419bd3661f1af513325.html
小菜总结,不足谢谢补充。
【CODE】
#include #include #include const int N=1005;
typedef __int64 lld;
struct edge{int y;edge *next;}g[1005555],*ls[N];
int n,e,cnt;
int v[N],link[N];
lld a[N];

int cmp(const void *A,const void *B){
lld AA=*((lld*)A),BB=*((lld*)B);
if (AA>BB) return 1;
if (AA return 0;
}

void addedge(int x,int y){
g[++e].y=y; g[e].next=ls[x]; ls[x]=&g[e];
}

void build(){
int tot=0,i,j;
for (i=1;i<=n;i++)
if (!tot || a[i]!=a[tot])
a[++tot]=a[i];
n=tot;
for (i=1;i<=n;i++)
for (j=i+1;j<=n;j++)
if (a[j]%a[i]==0 && a[j]>a[i])
addedge(i,j);
}

bool Find(int p){
for (edge *t=ls[p];t;t=t->next)
if (v[t->y]!=cnt){
v[t->y]=cnt;
int q=link[t->y];
link[t->y]=p;
if (!q || Find(q)) return true;
link[t->y]=q;
}
return false;
}

void solve(){
memset(link,0,sizeof(link));
memset(v,0,sizeof(v));
int ans=0;
for (cnt=1;cnt<=n;cnt++)
if (Find(cnt)) ans++;
printf("%dn",n-ans);
}

int main(){
int Tc;
scanf("%d",&Tc);
for (;Tc;Tc–){
scanf("%d",&n);
e=0;
for (int i=1;i<=n;i++){
scanf("%I64d",&a[i]);
ls[i]=NULL;
}
qsort(a+1,n,sizeof(lld),cmp);
build();
solve();
}
}