目录:
T1:Swapity Swap
T2:Triangles
T3:Clock Tree
同为三道USACO的题……
正题:
T1:Swapity Swap
题目描述
Farmer John 的 N 头奶牛(1≤N≤10^5)站成一排。对于每一个 1≤i≤N,从左往右数第 i 头奶牛的编号为 i。Farmer John 想到了一个新的奶牛晨练方案。他给奶牛们 M 对整数 (L1,R1)…(LM,RM),其中 1≤M≤100。他让她们重复以下包含 M 个步骤的过程 K(1≤K≤10^9)次:
对于从 1 到 M 的每一个步骤i:当前从左往右数在位置 Li…Ri 的奶牛序列反转她们的顺序。
当奶牛们重复这一过程 K 次后,请对每一个 1≤i≤N 输出从左往右数第 i 头奶牛的编号。
输入
输入的第一行包含 N, M 和 K。对于每一个 1≤i≤M,第 i+1 行包含 Li 和 Ri,均为范围在 1…N 内的整数,其中 Li<Ri。
输出
在第 i 行输出指令序列执行了 K 次后奶牛序列中从左往右数第 i 个元素的编号。
样例输入
7 2 2
2 5
3 7
样例输出
1
2
4
3
5
7
6
分析:
我们用 f[i][j] 记录第 j 个元素,经过2^i次翻转后,这个元素的值。
然后写倍增,拆分K,最后输出。
CODE:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,m,k,a[111],b[111],c[100010],f[35][100010];
int main(){
freopen("swap.in","r",stdin);
freopen("swap.out","w",stdout);
cin>>n>>m>>k;
for(int i=1;i<=m;i++)cin>>a[i]>>b[i];//读入
for(int i=1;i<=n;i++)c[i]=i; //赋初值
for(int i=1;i<=m;i++)reverse(c+a[i],c+b[i]+1);//反转字符串,模拟
for(int i=1;i<=n;i++)f[0][i]=c[i]; //经过1次翻转第i个元素的值为c[i]
for(int i=1;i<=30;i++)
for(int j=1;j<=n;j++)
f[i][j]=f[i-1][f[i-1][j]]; //倍增
for(int i=1;i<=n;i++){
int x=i,m=k;
for(int j=30;j>=0;j--)
if(m>=(1ll<<j)){
m-=(1ll<<j); //拆分
x=f[j][x];
}
cout<<x; //输出
}
return 0;
}
T2:Triangles
题目描述
Farmer John 想要给他的奶牛们建造一个三角形牧场。有 N(3≤N≤10^5)个栅栏柱子分别位于农场的二维平面上不同的点 (X1,Y1)…(XN,YN)。他可以选择其中三个点组成三角形牧场,只要三角形有一条边与 x 轴平行,且有另一条边与 y 轴平行。
FJ 可以组成的所有可能的牧场的面积之和等于多少?
输入
第一行包含 N。
以下 N 行每行包含两个整数 Xi 和 Yi,均在范围 −104…104 之内,描述一个栅栏柱子的位置。
输出
由于面积之和不一定为整数且可能非常大,输出面积之和的两倍模 10^9+7 的余数。
样例输入
4
0 0
0 1
1 0
1 2
样例输出
3
分析:
这道题有此公式:
ac+ad+bc+bd=(a+b)*(c+d)
根据这个可以:
按照x坐标从小到大,y坐标从小到大的顺序,依次去枚举每个点i,
记录每个横纵坐标各自积累的和是多少。当又来了一个新点,则把这个点对应的横坐标位置的和,加上4倍与上一个点之间的距离。
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long n,m,sum;
struct node{
long long x,y; //横、纵坐标
}a[100010];
bool cmp1(node a,node b){
return(a.x!=b.x)?a.x<b.x:a.y<b.y; //根据不同的关键词排序
}
bool cmp2(node a,node b){
return(a.x!=b.x)?a.x>b.x:a.y<b.y;
}
bool cmp3(node a,node b){
return(a.x!=b.x)?a.x<b.x:a.y>b.y;
}
bool cmp4(node a,node b){
return(a.x!=b.x)?a.x>b.x:a.y>b.y;
}
void work(){
long long xx[20010]={},yy[20010]={};
long long x1[20010]={},y1[20010]={};
long long x2[20010]={},y2[20010]={};
for(int i=1;i<=n;i++){
long long l=a[i].x;
long long r=a[i].y;
x2[l]++; //相同数的数量
x1[l]=(x1[l]+abs(r-xx[l])*(x2[l]-1))%1000000007; //计算差
xx[l]=r;//更新
y2[r]++;
y1[r]=(y1[r]+abs(l-yy[r])*(y2[r]-1))%1000000007;
yy[r]=l;
//同上
sum=(sum+x1[l]*y1[r])%1000000007;//公式原理
}
}
int main(){
freopen("triangles.in","r",stdin);
freopen("triangles.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
a[i].x+=10000; //数组
a[i].y+=10000;
}
sort(a+1,a+1+n,cmp1); //排序一次做一次
work();
sort(a+1,a+1+n,cmp2);
work();
sort(a+1,a+1+n,cmp3);
work();
sort(a+1,a+1+n,cmp4);
work();
cout<<sum;
return 0;
}
T3:Clock Tree
题目描述
Farmer John 的新牛棚的设计十分奇怪:它由编号为 1…N 的 N 间房间(2≤N≤2500),以及 N−1 条走廊组成。每条走廊连接两间房间,使得每间房间都可以沿着一些走廊到达任意其他房间。
牛棚里的每间房间都装有一个在表盘上印有标准的整数 1…12 的圆形时钟。然而,这些时钟只有一根指针,并且总是直接指向表盘上的某个数字(它从不指向两个数字之间)。
奶牛 Bessie 想要同步牛棚中的所有时钟,使它们都指向整数 12。然而,她头脑稍有些简单,当她在牛棚里行走的时候,每次她进入一间房间,她将房间里的时钟的指针向后拨动一个位置。例如,如果原来时钟指向 5,现在它会指向 6,如果原来时钟指向 12,现在它会指向 1。如果 Bessie 进入同一间房间多次,她每次进入都会拨动这间房间的时钟。
请求出 Bessie 可能的出发房间数量,使得她可以在牛棚中走动并使所有时钟指向 12。注意 Bessie 并不拨动她出发房间的时钟,但任意时刻她再次进入的时候会拨动它。时钟不会自己走动;时钟只会在 Bessie 进入时被拨动。此外,Bessie 一旦进入了一条走廊,她必须到达走廊的另一端(不允许走到一半折回原来的房间)。
输入
输入的第一行包含 N。下一行包含 N 个整数,均在范围 1…12 之内,表示每间房间初始时的时钟设置。以下 N−1 行每行用两个整数 a 和 b 描述了一条走廊,两数均在范围 1…N 之内,为该走廊连接的两间房间的编号。
输出
输出出发房间的数量,使得 Bessie 有可能使所有时钟指向 12。
样例输入
4
11 10 11 11
1 2
2 3
2 4
样例输出
1
分析:
求起点有多少个,那么我们依次枚举每个点作为起点,把它当做树根,从它出发往孩子走,看看能不能做到。
这样枚举每个点做根,每次根确定以后,一遍dfs。
CODE:
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
int n,c[2501],t[2501],ans;
vector<int> a[2501]; //存图
void dfs(int u,int f){ //dfs
for(int i=0;i<a[u].size();i++){
int v=a[u][i];
if(v==f) continue; //递归孩子节点
dfs(v,u);
//求出当前点值
t[u]=(t[u]-t[v]+12)%12;
}
}
int main() {
freopen("clocktree.in","r",stdin);
freopen("clocktree.out","w",stdout);
scanf("%d", &n);
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
c[i]%=12;
}
for(int i=0;i<n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
a[u].push_back(v);
a[v].push_back(u);
}
for(int i=1;i<=n;i++){
memcpy(t,c,sizeof(t));
//拷贝一遍c数组到t数组里,避免出错
dfs(i,0);
//最后根上剩了0或1都可以
if(t[i]==0||t[i]==1) ans++;
}
cout<<ans<<endl;
return 0;
}