唉!弱渣都没机会去比赛!
//代码还没提交过,不知道对不对,等题目挂出来再交。(已提交)
A.Secrete Master Plan
题意:给定2*2的矩阵A和B,问A是否能通过旋转得到B?
分析:模拟,把A转几下?
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
int a[10],b[10],temp[10];
void change()
{
temp[3]=a[1];
temp[4]=a[3];
temp[2]=a[4];
temp[1]=a[2];
a[1]=temp[1];
a[2]=temp[2];
a[3]=temp[3];
a[4]=temp[4];
}
bool check(int *t,int *f)
{
if(t[1]==f[1] && t[2]==f[2] && t[3]==f[3] && t[4]==f[4])
return true;
return false;
}
int main()
{
int ncase,i;
scanf("%d",&ncase);
for(int T=1;T<=ncase;T++)
{
scanf("%d%d%d%d",&a[1],&a[2],&a[3],&a[4]);
scanf("%d%d%d%d",&b[1],&b[2],&b[3],&b[4]);
for(i=1;i<=5;i++,change())
if(check(a,b))
break;
printf("Case #%d: %s\n",T,i>5?"IMPOSSIBLE":"POSSIBLE");
}
return 0;
}
C.The Battle of Chibi
题意:在长度为n的序列中,求长度为m的单调递增序列的个数?
分析:记dp[i][j]表示当前递增序列长度为i,最后一个数字的下标为j。用三层循环肯定不行。先把所有的数离散化,再用树状数组加快找下标在j前面,且值比a[j]小的长度为i-1的方案数。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1007;
const LL mod = 1e9+7;
int lowbit[maxn],a[maxn],f[maxn];
LL tree[maxn],dp[maxn][maxn];
void add(LL &a,LL b)
{
a+=b;
while(a>=mod)
a-=mod;
}
void Init()
{
for(int i=1;i<maxn;i++)
lowbit[i]=i&-i;
}
void update(int x,LL v)
{
for(int i=x;i<maxn;i+=lowbit[i])
add(tree[i],v);
}
LL query(int x)
{
LL ret(0);
for(int i=x;i>=1;i-=lowbit[i])
add(ret,tree[i]);
return ret;
}
int main()
{
Init();
int ncase,i,j,n,m;
scanf("%d",&ncase);
for(int T=1;T<=ncase;T++)
{
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
f[i]=a[i];
}
sort(f,f+n);
int tail=unique(f,f+n)-f;
for(i=0;i<n;i++)
a[i]=lower_bound(f,f+tail,a[i])-f+1;
for(i=0;i<n;i++)
dp[1][i]=1;
for(i=2;i<=m;i++)
{
memset(tree,0,sizeof(tree));
for(j=0;j<n;j++)
{
dp[i][j]=query(a[j]-1);
// printf("q:%d %lld\n",a[j]-1,query(a[j]-1));
update(a[j],dp[i-1][j]);
}
}
LL ans=0;
for(i=0;i<n;i++)
add(ans,dp[m][i]);
printf("Case #%d: %lld\n",T,ans);
}
return 0;
}
D.Pick The Sticks
题意:从n个金条里面选出一些来,放在一条长为L的线段上(不能重叠,并且保证金条的中心在线段上),使价值最大。问最大价值?
分析:假如金条必须完完全全放在线段上,那么这个题就是01背包了。但是这个题是允许最多两个金条可以只有一半放在线段上。记dp[i][j][0/1/2],表示处理了前i个金条,容量为j,有0/1/2个金条只有一半在线段上的最大价值。那么对于一个金条,有三种选择,不选、全部放在线段上和一半放在线段上。
代码:
</pre><pre name="code" class="cpp">#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
LL dp[2][4007][3];
int v[1007],w[1007];
void Max(LL &ans,LL &a)
{
ans=(ans>a?ans:a);
}
int main()
{
int ncase,i,j,k;
int L,n;
scanf("%d",&ncase);
for(int T=1;T<=ncase;T++)
{
scanf("%d%d",&n,&L);
L<<=1;
LL ans=0;
for(i=1;i<=n;i++)
{
scanf("%d%d",&w[i],&v[i]);
w[i]<<=1;
ans=ans>v[i]?ans:v[i];
}
int cur=0;
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
cur^=1;
for(j=0;j<=L;j++)
for(k=0;k<3;k++)
dp[cur][j][k]=dp[cur^1][j][k];
for(j=L;j>=w[i]/2;j--)
{
for(k=0;k<3;k++)
{
if(j>=w[i])
dp[cur][j][k]=max(dp[cur][j][k],dp[cur^1][j-w[i]][k]+v[i]);
if(k && j>=w[i]/2)
dp[cur][j][k]=max(dp[cur][j][k],dp[cur^1][j-w[i]/2][k-1]+v[i]);
Max(ans,dp[cur][j][k]);
}
}
}
printf("Case #%d: %lld\n",T,ans);
}
return 0;
}
E.ba Gua Zhen
题意:在一个连通图上找一个回路(从起点出发回到起点),使得回路上的所有经过的路径上的权值异或和最大。(如果一条路径走了x次,结果要异或x下)。
分析:①先把所有独立的回路的异或和找出来。②那么问题就转变为在n个数里面选任意多的数使得异或和最大。
对于①用dfs搜,搜的过程中记录当前位置由哪里来,使得不往回走。如果一个点被重复访问,说明形成回路。
对于②高斯消元
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1e5+7;
struct node
{
int id,next;
LL w;
}List[maxn<<4];
int head[maxn],cnt;
void Clear()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,LL w) // u-->v
{
List[cnt].id=v;
List[cnt].w=w;
List[cnt].next=head[u];
head[u]=cnt++;
}
LL XOR[maxn<<4],num,buf[maxn];//buf[i]表示从起点到i的异或和
bool visit[maxn];
void dfs(int cur,int per,LL value)
{
visit[cur]=true;
for(int i=head[cur];~i;i=List[i].next) if(List[i].id!=per)
{
int to=List[i].id;
LL w=List[i].w;
if(visit[to])
{
XOR[num++]=value^w^buf[to];
continue ;
}
buf[to]=value^w;
dfs(to,cur,value^w);
}
}
LL base[65];
LL guass(int n)
{
memset(base,0,sizeof(base));
for(int i=0;i<n;i++)
{
LL cur=XOR[i];
for(int j=59;j>=0;j--)
{
if((1ll<<j)&cur)
{
if(base[j]==0)
{
base[j]=cur;
break;
}
else
{
cur^=base[j];
}
}
}
}
LL ret=0;
for(int i=0;i<60;i++)
for(int j=i+1;j<60;j++)
if((base[j]>>i)&1)
base[j]^=base[i];
for(int i=59;i>=0;i--)
ret^=base[i];
return ret;
}
int main()
{
int nCase,i,j,u,v,n,m;
scanf("%d",&nCase);
for(int T=1;T<=nCase;T++)
{
scanf("%d%d",&n,&m);
Clear();
for(i=1;i<=m;i++)
{
LL w;
scanf("%d %d %lld",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
num=0;
memset(visit,0,sizeof(visit));
memset(buf,0,sizeof(buf));
dfs(1,-1,0);
printf("Case #%d: %lld\n",T,guass(num));
}
return 0;
}
G.Ancient Go
题意:给定一副围棋的局面,先将所有的已经死掉的'x'去掉,再将所有的已经死掉的'o'去掉,问在添加一个'x'是否能吃掉至少一个‘o’?
分析:先算出'x'的连通块边缘有多少个'.'与之相连,如果是0个就把这个连通块去掉,同理去掉‘o’。然后再算‘o’的连通块边缘有多少个'.'与之相连,若存在一个为1的,就满足条件,否则不满足。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
char maze[12][12];
set <int > st;
bool visit[100][100];
void Find(int x,int y,char ch)
{
if(visit[x][y])
return ;
visit[x][y]=1;
// printf("(%d,%d)\n",x,y);
if(maze[x+1][y]=='.')
st.insert((x+1)*10+y);
else if(maze[x+1][y]==ch)
Find(x+1,y,ch);
if(maze[x][y+1]=='.')
st.insert((x)*10+y+1);
else if(maze[x][y+1]==ch)
Find(x,y+1,ch);
if(maze[x-1][y]=='.')
st.insert((x-1)*10+y);
else if(maze[x-1][y]==ch)
Find(x-1,y,ch);
if(maze[x][y-1]=='.')
st.insert(x*10+y-1);
else if(maze[x][y-1]==ch)
Find(x,y-1,ch);
}
void Delete(int x,int y,char ch)
{
maze[x][y]='.';
if(maze[x+1][y]==ch)
Delete(x+1,y,ch);
if(maze[x][y+1]==ch)
Delete(x,y+1,ch);
if(maze[x-1][y]==ch)
Delete(x-1,y,ch);
if(maze[x][y-1]==ch)
Delete(x,y-1,ch);
}
void check()
{
int i,j;
printf("-------------------------------------------\n");
for(i=1;i<=9;i++)
{
for(j=1;j<=9;j++)
{
printf("%c",maze[i][j]);
}
printf("\n");
}
printf("\n");
}
int main()
{
// freopen("test.txt","r",stdin);
// freopen("ou.txt","w",stdout);
int ncase,n,m,i,j;
scanf("%d",&ncase);
for(int T=1;T<=ncase;T++)
{
for(i=1;i<=9;i++)
scanf("%s",maze[i]+1);
memset(visit,0,sizeof(visit));
for(i=1;i<=9;i++)
{
for(j=1;j<=9;j++)
{
if(maze[i][j]=='o' && !visit[i][j])
{
st.clear();
Find(i,j,'o');
if(st.size()==0)
Delete(i,j,'o');
}
}
}
// check();
memset(visit,0,sizeof(visit));
for(i=1;i<=9;i++)
{
for(j=1;j<=9;j++)
{
if(maze[i][j]=='x' && !visit[i][j])
{
st.clear();
Find(i,j,'x');
if(st.size()==0)
Delete(i,j,'x');
}
}
}
// check();
memset(visit,0,sizeof(visit));
bool ok=false;
for(i=1;i<=9 && !ok;i++)
{
for(j=1;j<=9 && !ok;j++)
{
if(maze[i][j]=='o' && !visit[i][j])
{
st.clear();
Find(i,j,'o');
if(st.size()==1)
ok=true;
}
}
}
printf("Case #%d: %s\n",T,ok?"Can kill in one move!!!":"Can not kill in one move!!!");
}
return 0;
}
H.Sudoku
题意:将4*4的矩阵上空缺的位置填上数字,使其称为Sudoku。输出矩阵。
分析:把空缺的位置的坐标存起来,然后dfs暴力枚举空缺的位置上的数,判断填的数是否合法。能填满就说明方案可行。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
struct node
{
int x,y;
}s[100];
int cnt,getIt;
char maze[10][10];
bool ok(int x,int y,char ch)
{
for(int i=1;i<=4;i++)
if(maze[x][i]==ch)
return false;
for(int i=1;i<=4;i++)
if(maze[i][y]==ch)
return false;
if(x<=2 && y<=2)
{
if(maze[1][1]==ch || maze[1][2]==ch || maze[2][1]==ch || maze[2][2]==ch)
return false;
}
if(x>2 && y<=2)
{
if(maze[3][1]==ch || maze[3][2]==ch || maze[4][1]==ch || maze[4][2]==ch)
return false;
}
if(x<=2 && y>2)
{
if(maze[1][3]==ch || maze[1][4]==ch || maze[2][3]==ch || maze[2][4]==ch)
return false;
}
if(x>2 && y>2)
{
if(maze[3][3]==ch || maze[3][4]==ch || maze[4][3]==ch || maze[4][4]==ch)
return false;
}
return true;
}
void DFS(int cur)
{
if(getIt)
return ;
if(cur>=cnt)
{
for(int i=1;i<=4;i++)
{
for(int j=1;j<=4;j++)
putchar(maze[i][j]);
putchar('\n');
}
getIt=1;
return ;
}
int nx=s[cur].x,ny=s[cur].y;
char buf=maze[nx][ny];
for(char ch='1';ch<='4';ch++) if(ok(nx,ny,ch))
{
maze[nx][ny]=ch;
DFS(cur+1);
maze[nx][ny]=buf;
}
}
int main()
{
// freopen("data.txt","r",stdin);
// freopen("out.txt","w",stdout);
int ncase,i,j;
scanf("%d",&ncase);
for(int T=1;T<=ncase;T++)
{
getIt=cnt=0;
for(i=1;i<=4;i++)
{
scanf("%s",maze[i]+1);
for(j=1;j<=4;j++)
{
if(maze[i][j]=='*')
{
s[cnt].x=i;
s[cnt++].y=j;
}
}
}
printf("Case #%d:\n",T);
DFS(0);
}
return 0;
}
K.Game Rooms
题意:有n层楼,现在每一层楼可以建两种设施里面的一种,已知每层楼里面分别想玩两种设施的人数,每个人的消耗为离他最近的楼层(楼层里面有他想玩的设施)的距离,现在问怎样建设施,使得总的消耗最小。
分析:定义dp[i][j][type],表示当前楼层为i,与之最近且不同的楼层为j(j<i),j建type设施。由当前楼层推到下一层的状态转移为:
j i i+1
$ * * * * * * *
①下一层也建type类型的设施,dp[i+1][j][type]=min(dp[i+1][j][type],dp[i][j][type]); //j~i+1的消耗暂时先不算
j i i+1
$ * * * * * * $
②下一层建另外一种类型的设施:dp[i+1][i][type^1]=min(dp[i+1][i][type^1],dp[i][j][type]+v(j+1~i的消耗))
//j+1~i这一段的消耗=前一半到j层的消耗+后一半到i+1层的消耗
ps:消耗需要先预处理出来,O(1)查询。还有最后要把没有加完的部分加上。
特殊情况:********$,dp[i+1][i][type]=(前面所有*到$的消耗)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E18+9;
const int maxn = 4007;
LL dp[2][maxn][2];
LL Prefix[maxn][2],Suffix[maxn][2],Sum[maxn][2];
LL a[maxn],b[maxn],n;
void Init(int n)
{
int i,j,c;
Prefix[0][0]=Prefix[0][1]=0;
for(i=1;i<=n;i++)
{
Prefix[i][0]=Prefix[i-1][0]+a[i]*i;
Prefix[i][1]=Prefix[i-1][1]+b[i]*i;
}
Suffix[n+1][0]=Suffix[n+1][1]=0;
for(i=n,c=1;i>=1;i--,c++)
{
Suffix[i][0]=Suffix[i+1][0]+a[i]*c;
Suffix[i][1]=Suffix[i+1][1]+b[i]*c;
}
}
inline LL getPrefix(int type,int L,int R)
{
if(L>R) return 0;
LL ret=Prefix[R][type]-Prefix[L-1][type];
return ret-(Sum[R][type]-Sum[L-1][type])*(L-1);
}
inline LL getSuffix(int type,int L,int R)
{
if(L>R) return 0;
LL ret=Suffix[L][type]-Suffix[R+1][type];
return ret-(Sum[R][type]-Sum[L-1][type])*(n-R);
}
int main()
{
// freopen("test.txt","r",stdin);
// freopen("oyx.txt","w",stdout);
int ncase,i,j,k;
scanf("%d",&ncase);
for(int T=1;T<=ncase;T++)
{
scanf("%d",&n);
Sum[0][0]=Sum[0][1]=0;
for(i=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);
Sum[i][0]=Sum[i-1][0]+a[i];
Sum[i][1]=Sum[i-1][1]+b[i];
}
Init(n);
int cur=0;
for(j=1;j<=n;j++)
dp[cur][j][0]=dp[cur][j][1]=INF;
dp[cur][1][0]=b[1]; //现在的cur表示第2层 ,处理了第cur-1=1层
dp[cur][1][1]=a[1];
for(i=2;i<n;i++)
{
cur^=1;
for(j=1;j<=i;j++)
for(k=0;k<2;k++)
dp[cur][j][k]=INF;
dp[cur][i][0]=getSuffix(1,1,i);
dp[cur][i][1]=getSuffix(0,1,i);
for(j=1;j<i;j++)
{
for(k=0;k<2;k++)
{
LL v=dp[cur^1][j][k];
if(v>=INF)
continue ;
dp[cur][j][k]=min(dp[cur][j][k],v);
int mid=(j+1+i)>>1;
LL temp=getPrefix(k,j+1,mid)+getSuffix(k,mid+1,i);
dp[cur][i][k^1]=min(dp[cur][i][k^1],v+temp);
}
}
}
LL ans=INF,temp;
for(j=1;j<n;j++)
{
temp=getPrefix(0,j+1,n);
ans=min(ans,dp[cur][j][0]+temp);
temp=getPrefix(1,j+1,n);
ans=min(ans,dp[cur][j][1]+temp);
}
printf("Case #%d: %lld\n",T,ans);
}
return 0;
}
L.Huatuo's Medicine
题意:Huatuo有很多药和很多相同的瓶子,huatuo把药瓶子串成一条链,现在他只知道每种药的位置(不知道从哪头开始),问假如有n种药,这条链最短多长他能准确的取出他想要的药?
分析:2*n-1?
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
int main()
{
int ncase,x;
scanf("%d",&ncase);
for(int T=1;T<=ncase;T++)
{
scanf("%d",&x);
printf("Case #%d: %d\n",T,x*2-1);
}
return 0;
}