POJ 1222(Gauss消元xor版)

EXTENDED LIGHTS OUT
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 5182   Accepted: 3403

Description

Lights Out就是下图的游戏,给你一个5*6的矩阵. 


你的目标是把灯全关上. 



0表示关,1表示开.

Input

第一行为数据组数T.
对于每组数据,输入1个5*6的矩阵.

Output

对于每组数据,输出一行 "PUZZLE #m".接下来为还原矩阵.

Sample Input

2
0 1 1 0 1 0
1 0 0 1 1 1
0 0 1 0 0 1
1 0 0 1 0 1
0 1 1 1 0 0
0 0 1 0 1 0
1 0 1 0 1 1
0 0 1 0 1 1
1 0 1 1 0 0
0 1 0 1 0 0

Sample Output

PUZZLE #1
1 0 1 0 0 1
1 1 0 1 0 1
0 0 1 0 1 1
1 0 0 1 0 0
0 1 0 0 0 0
PUZZLE #2
1 0 0 1 1 1
1 1 0 0 0 0
0 0 0 1 0 0
1 1 0 1 0 1
1 0 1 1 0 1

Source

就是Gauss消元的xor版本.

显然若 a1 xor a2 xor a3..=an

             b1 xor b2 xor b3..=bn

        则 (a1 xor b1) xor (a2 xor b2)..=an xor an

满足消元性质

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<functional>
using namespace std;
const int n=5,m=6;
bool inside(int x){return (x>=0)&&(x<31);}
struct E
{
	int a[31];
	int& operator[](int p){static int t;if (inside(p)) return a[p]; else return t;}
	friend E operator^(E a,E b)
	{
		for (int i=0;i<31;i++) a[i]^=b[i];
		return a;
	}
};
struct M
{
	E a[30];
	E& operator[](int p){return a[p];}
	M()
	{
	//	memset(a,sizeof(a),0);
		for (int i=0;i<30;i++)
			for (int j=0;j<31;j++) a[i][j]=0;
		for(int i=0;i<n*m;i++)
		{
			a[i][i]=a[i][i+6]=a[i][i-6]=1;
			if (i%6) a[i][i-1]=1;
			if (i%6<5) a[i][i+1]=1;

		}
	}
	void gauss()
	{
		int i=0,j=0,n=30,m=31;
		while (i<n&&j<m)
		{
//			print();
			int maxi=i;
			for(int k=i+1;k<n;k++) if (a[k][j]>a[maxi][j]) maxi=k;
			if (a[maxi][j]!=0)
			{
				swap(a[maxi],a[i]);
				for(int k=i+1;k<n;k++)
					if (a[k][j]!=0) a[k]=a[k]^a[i];
			}
			i++;j++;
		}
		for(int i=28;i>=0;i--)
		{
			for(int j=i+1;j<30;j++) if (a[i][j]) {a[i][j]=0;a[i][30]^=a[j][30];	}
		}
		for (int i=0;i<30;i++)
		{
			cout<<a[i][30];
			if (i%6==5) cout<<endl; else cout<<' ';
		}
	}
	void print()
	{
		for (int i=0;i<30;i++)
		{
			for (int j=0;j<31;j++) cout<<a[i][j]<<' ';
			cout<<endl;
		}
		cout<<"--------------------------------------------------n";
		system("pause");
	}


}EM;
int main()
{
//	freopen("poj1222.in","r",stdin);
	int tt;
	cin>>tt;
	for(int t=1;t<=tt;t++)
	{
		EM=M();
		for(int i=0;i<n*m;i++) cin>>EM[i][30];
		cout<<"PUZZLE #"<<t<<endl;
		EM.gauss();


	}
	return 0;
}



fzu_noip 1040(继任者-离线排序插入+区间最值)

继任者

时限:1s内存:32M

★问题描述:

S开了家公司,他自己是老板,其余员工都有一名直系上级,每名员工都有对公司的忠诚度和能力值。S时不时会开除某名员工,由其下属中能力值大于这名员工的人中忠诚度最高的担任其职务。他想知道,若开除某名员工,该选谁担任其职务。A是B的下属是指,A的直系上级是B或B的下属。

★数据输入:

第一行输入T表示CASE数。接下来T组数据,每组数据第一行输入两个整数n,m(2<=n,m<=25000),表示有该公司包括S有n个人,有m组询问。员工按输入顺序从1到n-1编号,S编号为0。接下去1到n-1行,每行三个整数a,b,c(0<=a<=n-1,0<=b,c<=1000000)分别表示第i名员工的直系上级编号、忠诚度、能力值,每名员工的编号大于其上级的编号,且他们的忠诚度各不相同。接下去m行,每行一个整数q(1<=q<=n-1)表示询问若开除第q名员工应该选谁担任其职务。注意询问并没有真正开除员工,也就是说各个询问假设要开除的员工互不影响。

★结果输出:

对于每个询问输出一个数表示该选的员工的编号,若这样的员工不存在则输出-1。

输入示例

输出示例

1

3 2

0 100 99

1 101 100

1

2

2

-1

 本来是按B值对结点从大到小排序-然后按顺序插入线段树

因为插入前结点所对应的区间一定只有比父节点B值大的,因此直接输出A值最大的即可(线段树)

实际做法:暴力插入结点,直到找到根或找到比它A,B值都大(这样上司选它一定更优)的为止。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (25000+10)
#define MAXCi (1000000)
int n,m;
struct node
{
    int key,fim,fa,ans;
    node():ans(-1){}
    node(int _key,int _fim,int _fa):key(_key),fim(_fim),fa(_fa),ans(-1){}
}a[MAXN];
void update(int x)
{
     for (int now=a[x].fa;now;now=a[now].fa)
     {
         if (a[x].key>a[now].key&&(a[now].ans==-1||a[a[now].ans].fim<a[x].fim)) a[now].ans=x;
         else if (a[x].key<a[now].key&&a[x].fim<a[now].fim) break;
     }
}
int main()
{
    cin>>n>>m;
    for (int i=1;i<=n-1;i++)
    {
        cin>>a[i].fa>>a[i].fim>>a[i].key;update(i);
    }
    for (int i=1;i<=m;i++)
    {
        int p;
        cin>>p;
        cout<<a[p].ans<<endl;
    }
    return 0;
}

fzu_noip 1039(盖楼-线段树)

盖楼

时限:1s内存:32M

★问题描述:

S举办了一场盖楼比赛,有n位选手参赛,将这n位选手编号为1到n。比赛刚开始时第i位选手的房子的初始高度为Ai,每过一天该选手的房子高度增加Bi。S想知道比赛开始后T天编号为L到R的选手中,造的最高的房子高度为多少。

★数据输入:

输入数据的第一行为两个整数N,Q。(1<=N,Q<=30000)。接下来N行,表示每个选手的初始房子高度和房子每天增长高度,每行两个数Ai,Bi(1<=Ai,Bi<=10^9)。接下来Q行,表示Q个询问,每行3个数Li,Ri,Ti(1<=Li<=Ri<=N,0<=Ti<=1000000)。所有输入都是整数。

★结果输出:

对于每个询问输出一行一个整数,表示比赛开始后Ti天编号为Li到Ri的选手中造的房子的最大高度。

 

输入示例

输出示例

5 4

4 1

3 5

6 2

3 5

6 5

1 5 2

1 3 5

1 1 0

1 5 0

16

28

4

6

5 4

6 1

5 1

2 5

4 3

6 1

2 4 1

3 4 5

1 4 5

1 2 0

7

27

27

6

 

本来是半平面交+线段树合并(归并合并),暴力居然能过


const
   maxn=30000;
var
   n,q,i,j,l,r,p,t:longint;
   a,b:array[1..maxn] of longint;
begin
   read(n,q);
   for i:=1 to n do read(a[i],b[i]);
   for i:=1 to q do
   begin
      read(l,r,t);
      p:=b[l]*t+a[l];
      for j:=l+1 to r do
         if (p<b[j]*t+a[j]) then p:=b[j]*t+a[j];
      writeln(p);
   end;
end.

fzu_noip 1036(磁盘碎片整理-Dp)

磁盘碎片整理

时限:1s
内存:32M

★问题描述:

Jack最近在PS海报。海报所需各种素材不但让Jack头大,也让硬盘分区中的文件碎片越来越多,电脑的反应速度越来越慢。

烦恼的Jack决定好好整理一下磁盘的碎片。但是,为了体现高手风范,自负的Jack不喜欢系统自带的整理工具,决定自己编写一个,希望你帮他完成简单的第一遍整理。

因为不可移动的系统文件的分隔,Jack的N个文件,分为许多文件块,杂乱分布在M个长度不等的区间中。在第一遍整理时,只将每个区间中的碎片进行顺序改变,不跨区间移动。

因为在Jack完成的第二次整理时,将把M个区间依顺序连接成一个完整区间,希望整理完成后,零碎度越低越好。(若连接后的完整区间,文件块i与文件块i+1属于不同文件,则零碎度+1)。

例:

N=5 M=3

存储区间1:长度3 文件块[4,3,1]

存储区间2:长度4 文件块[2,1,4,4]

存储区间3:长度3 文件块[1,3,1]

第一次整理,第二次连接后:[1,3,4][4,4,2,1][1,1,3],零碎度为5

★数据输入:

第一行n m(1<=n<=10,1<=m<=10000),表示有n个文件,m个存储区间。

接下来m行,每行第一个整数为Li(0<Li<=50),表示存储区间i的长度。接下来Li个整数,分别表示该区间第j文件块所属文件。

★结果输出:

输出两次整理完成后,能达到的最小零碎度。

输入示例

输出示例

5 3

3 4 3 1

4 2 1 4 4

3 1 3 1

5

 

 

简单Dp,f[i][j]表示到第i块硬盘前面接j能取得的最大头尾连接数(开始那个默认与之前的连接),则


F[i][j]=| max(f[i][l[j]],f[i-1][k]+(b[i-1][l[j]]&&(k!=l[j]||pre_size==1))) (i>1)

          |1
(i=1)

答案为tot-max(f[m][k](0<=k<n)) tot为每个区间不同数字类数的总和

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
using namespace std;
#define MAXN (10)
#define MAXM (10000+10)
#define MAXLi (50)
int n,m;
bool b[MAXM][11];
int f[MAXM][11];
int l[MAXN];
int main()
{
  //  freopen("piece.in","r",stdin);
    memset(b,0,sizeof(b));
    memset(f,0,sizeof(f));
    scanf("%d%d",&n,&m);
    int ans=0,pre_size=0;
    for (int i=1;i<=m;i++)
    {
        int li;
        scanf("%d",&li);
        for (int j=1;j<=li;j++) scanf("%d",&l[j]);
        sort(l+1,l+li+1);
        int size=unique(l+1,l+li+1)-(l+1);
        ans+=size;
    //  cout<<size<<endl;
    //  for (int i=1;i<=size;i++) cout<<l[i]<<' ';cout<<endl;
        for (int j=1;j<=size;j++)
            b[i][l[j]]=1;
        if (i==1)
        {
           for (int j=1;j<=size;j++)
               f[1][l[j]]=1;
        }
        else
        {
        for (int j=1;j<=size;j++)
            for (int k=0;k<n;k++)
                if (b[i-1][k])
                {
                   f[i][l[j]]=max(f[i][l[j]],f[i-1][k]+(b[i-1][l[j]]&&(k!=l[j]||pre_size==1)));
                }
        }
        pre_size=size;
    }
    int p=0;
    for (int j=0;j<n;j++) {p=max(p,f[m][j]); }
/*
    for (int i=1;i<=m;i++)
    {
        for (int j=0;j<n;j++)
            cout<<f[i][j]<<' ';
        cout<<endl;
    }
*/
    cout<<ans-p<<endl;
//   while (1);
    return 0;
}


fzu_noip 1033 (作业问题-拼最大的2,3,5倍数)

作业问题

时限:1s内存:32M

★问题描述:

小T很喜欢数学,每天老师刚布置完作业,他就开始思考,今天他遇到了困难。

现在有很多的数字,你的任务是找出由这些数字组成的最大的数,并且这个数必须能被2,3,5整除。你可以只用其中一部分的数,但不允许出现前导0。

★数据输入:

输入数据的第一行为一个整数N。(1<=N<=1000)表示给出N个数字,每个数字范围是0—9。接下来一行有N个数,数字由空格隔开。

★结果输出:

输出你找出的最大的数,如果不存在这样的数就输出-1。

输入示例

输出示例

1

0

0

11

3 4 5 4 5 3 5 3 4 4 0

5554443330

8

3 2 5 1 5 2 2 3

-1

 

裸裸的贪心。

满足能被2,5整除,当且仅当数中有0且放在末尾

满足能被3整除,要让sum%3=0.

显然要取尽可能多的位数,先判断是否能全取。

用a[i]表示数字i有几个,b[i]表示%3=i的数字有几个.

若全取后%3=1,则删1个%3=1或2个%3=2.优先删删除数字少的,次优先删数字尽量小的。

若无法全取,也不够删,则无解.

同理%3=2.

若有合法解,数字必从大到小排列。

由于不能有前导0,需特判0,0,0,0……的情况。

var
   a:array[0..9] of longint;
   b:array[0..2] of longint;
   n,i,j,p:longint;
procedure decrease(k:longint);
begin
   if (a[k]>0) then dec(a[k])
   else if (a[k+3]>0) then dec(a[k+3])
   else if (a[k+6]>0) then dec(a[k+6]);
end;
begin
   read(n);
   fillchar(a,sizeof(a),0);
   for i:=1 to n do
   begin
      read(j);
      inc(a[j]);  inc(b[j mod 3]);
   end;
   if (a[0]=0) then
   begin
      writeln('-1');
      halt;
   end;

   p:=b[1] mod 3+(b[2]*2) mod 3;
   if (p=1) then
   begin
      if (b[1]>0) then decrease(1)
      else if (b[2]>1) then begin decrease(2);decrease(2); end
      else
      begin
         writeln('-1');
         halt;
      end;
   end;
   if (p=2) then
   begin
      if (b[2]>0) then decrease(2)
      else if (b[1]>1) then begin decrease(1);decrease(1); end
      else
      begin
         writeln('-1');
         halt;
      end;
   end;

   p:=0;
   for i:=1 to 9 do inc(p,a[i]);
   if (p=0) then
   begin
      writeln('0');
      halt;
   end;


   for i:=9 downto 0 do
      for j:=1 to a[i] do write(i);
   writeln;







end.

fzu_noip 1032 (无穷数-进位判定)

无穷数

时限:1s内存:32M

★问题描述:

我们生成两个无穷大的数,第一个数是把所有的自然数链接起来组成的数字;第二个数是把所有自然数的平方连接起来组成的数。对这两个数求和,如下:

 123456789101112131415161718192021...

+ 149162536496481100121144169196225...

= 272619325597593231536305887388246...

现在给你一个整数k,问和从左往右数第k位的数码是多少?

★数据输入:

输入数据有多组,每组数据输入一行,有一个数k。对于100%的数据,k<=2147483647

★结果输出:

对于每组数据,输出一个整数N,从左往右数第k位的数码。

输入示例

输出示例

5

6

7

8

1

9

3

2

 
先算出第k位的Ai和Bi,然后相加,考虑是否加过头。
接下来用search_fi(k:longint) 判断第k位是否会令前一位进位,则


const
   a:array[1..19,1..2] of int64=((1,3),(4,9),(10,31),(32,99),(100,316),(317,999),(1000,3162),(3163,9999),(10000,31622),(31623,99999),(100000,316227),(316228,999999),(1000000,3162277),(3162278,9999999),(10000000,31622776),(31622777,99999999),
   (100000000,316227766),(316227767,999999999),(1000000000,2147483647));
var
   k:longint;
function search_ai(k:longint):longint;
var
   i,j,d,k2,g:int64;
begin
   d:=1;i:=9;
   while (true) do
   begin
      if (k-d*i>0) then
      begin
         dec(k,d*i);
         i:=i*10;inc(d);
      end
      else break;
   end;
   k2:=i div 9+(k-1) div d;
   g:=(k-1) mod d+1;
   g:=d-g+1;
   while (g>1) do begin k2:=k2 div 10;dec(g); end;
   exit(k2 mod 10);
end;
{
function search_bi(k:longint):longint;
var
   i,j:int64;
   head:longint;
begin
   head:=1;j:=10; i:=1;
   while (i<=2147483647 ) do
   begin
      if ((i*i) div j>0) then
      begin
         write('(',head,',',i-1,')',',');
         head:=i; j:=j*10;
      end;
      inc(i);
   end;
end;      }


function search_bi(k:longint):longint;
var
   i:longint;
   g,k2:int64;
begin
   i:=1;
   while (k>i*(a[i,2]-a[i,1]+1)) do
   begin
      dec(k,i*(a[i,2]-a[i,1]+1));
      inc(i);
   end;

   k2:=(k-1) div i+1;
   k2:=a[i,1]-1+k2;
   k2:=k2*k2;
   g:=(k-1) mod i+1;
   g:=i-g+1;

   while (g>1) do begin k2:=k2 div 10;dec(g); end;
   exit(k2 mod 10);
end;
function search_fi(k:longint):longint;
var
   i:longint;
begin
   i:=search_ai(k)+search_bi(k);
   if (i<=8) then exit(0)
   else if (i>9) then exit(1)
   else exit(search_fi(k+1));

end;
begin
//   assign(output,'dabiao.out');
//   rewrite(output);

   while not eof do
   begin
      readln(k);
      writeln((search_bi(k)+search_ai(k)+search_fi(k+1)) mod 10);


   end;

//   search_bi(1);
//close(output);
end.