链接:https://ac.nowcoder.com/acm/contest/3002/C
来源:牛客网
umi对弓道非常痴迷。
有一天,她在研究一个射箭问题:
在一个无限大的平面中,她站在 (x0,y0) 这个坐标。
有 n个靶子,第i 个靶子的坐标是(xi,yi)
umi准备在 x轴或y 轴上放置一块挡板来挡住弓箭的轨迹,使得她可以射中的靶子数量不超过k 个。
她想知道挡板的最短长度是多少?
注:假定弓箭的轨迹是起点为umi坐标、长度无穷大的射线。umi和靶子的体积可以无视。挡板的边缘碰到弓箭轨迹也可视为挡住弓箭。
注2:挡板不能弯折,起始和终点必须在同一坐标轴上。
一开始看到这个题目跟失了智一样。。。完全没有想法,但其实很简单。每一条射线都会跟X轴或者Y轴存在一个交点,除非与umi同象限,分别将X轴和Y轴上的交点记录下来,然后维护一个长度为K的平板(只在X轴或者Y轴上跨越K个点),不断移动记录最小长度即可。
#include
#include
#include
using namespace std;
vector<double> x;
vector<double> y;
double tx,ty;
int n,k;
void input(){
scanf("%lf%lf",&tx,&ty);
scanf("%d%d",&n,&k);
k = n - k;
double tempx,tempy;
for(int i = 0;i < n;i++){
scanf("%lf%lf",&tempx,&tempy);
if(tempx * tx < 0){
x.push_back(ty - tx * 1.0 * (ty - tempy) / (tx - tempx));
}
if(tempy * ty < 0){
y.push_back(tx - ty * (tx - tempx) / (ty - tempy));
}
}
}
void work(){
double mi = 1e18;
sort(x.begin(),x.end());
sort(y.begin(),y.end());
if(x.size() >= k){
int head =0,tail = k - 1;
while(tail < x.size()){
mi = min(mi,x[tail] - x[head]);
tail++;
head++;
}
}
if(y.size() >= k){
int head = 0,tail = k - 1;
while(tail < y.size()){
mi = min(mi,y[tail] - y[head]);
tail++;
head++;
}
}
if(mi == 1e18){
printf("-1");
}else{
printf("%.8lf",mi);
}
}
int main(){
input();
work();
return 0;
}
链接:https://ac.nowcoder.com/acm/contest/3002/F
来源:牛客网
有一天,maki拿到了一颗树。所谓树,即没有自环、重边和回路的无向连通图。
这个树有 n 个顶点,n - 1条边。每个顶点被染成了白色或者黑色。
maki想知道,取两个不同的点,它们的简单路径上有且仅有一个黑色点的取法有多少?
注:
①树上两点简单路径指连接两点的最短路。
②
和 的取法视为同一种。
路径上有且仅有黑色点的一共有两种情况,一种是一个端点是黑色,一个端点是白色,或者两个端点都是白色。那么对于第一种情况,我们只需要知道一个黑色端点下总共连接了多少个白色的点就够了,对于第二种情况,则只要记录下每一个与黑色端点直接相连的白色端点所连接的白色点的个数,俩俩相乘并求和就是最后的答案。我们假设第i个白点的权值(即所连接白色点的个数)为 f ( i ) f(i) f(i),假设一个黑端点连接了K个白点
第一个路径就是 ∑ i = 1 k f ( i ) \sum_{i = 1}^kf(i) ∑i=1kf(i)第二个路径的数量为 ∑ i = 1 k ∑ j = i + 1 k f ( i ) f ( j ) \sum_{i = 1}^k\sum_{j = i + 1}^kf(i)f(j) ∑i=1k∑j=i+1kf(i)f(j)
#include
#include
using namespace std;
int n;
const int maxn = 1e5 + 7;
vector<int> tree[maxn];
bool flag[maxn] = {false};
int root[maxn];
int numSon[maxn];
int t[maxn];
int find_root(int n){
if(n == root[n]){
return n;
}
return find_root(root[n]);
}
void uni(int x,int y){
int tempx = find_root(x);
int tempy = find_root(y);
if(tempx != tempy){
if(numSon[tempx] > numSon[tempy]){
root[tempy] = tempx;
numSon[tempx] += numSon[tempy] + 1;
}else{
root[tempx] = tempy;
numSon[tempy] += numSon[tempx] + 1;
}
}
}
void input(){
scanf("%d",&n);
getchar();
for(int i = 1;i <= n;i++){
if(getchar() == 'B'){
flag[i] = true;
}
root[i] = i;
numSon[i] = 0;
}
int a,b;
for(int i = 0;i < n - 1;i++){
scanf("%d%d",&a,&b);
tree[a].push_back(b);
tree[b].push_back(a);
//白色端点并查集合并根,便于后面处理
if(!flag[a] && !flag[b]){
uni(a,b);
}
}
}
long long int getSum(vector<int> temp){
long long int res = 0;
int size = temp.size();
if(size == 0){
return 0;
}
long long int dp[maxn] = {0};
long long int sum[maxn] = {0};
//一个端点为白,一个端点为黑
for(int i = 0;i < size;i++){
res += temp[i];
}
sum[0] = temp[0];
//两个端点都是白色的
for(int i = 1;i < size;i++){
sum[i] = sum[i - 1] + temp[i];
}
for(int i = 1;i < size;i++){
dp[i] = dp[i-1] + temp[i] * sum[i-1];
}
return res + dp[size - 1];
}
void work(){
long long int sum = 0;
for(int i = 1;i <= n;i++){
t[i] = numSon[find_root(i)] + 1;
}
for(int i = 1;i <= n;i++){
if(flag[i]){
vector<int> temp;
for(int j = 0;j < tree[i].size();j++){
//找到所有与这个黑色端点相连的白色点
if(!flag[tree[i][j]]){
temp.push_back(t[tree[i][j]]);
}
}
sum += getSum(temp);
}
}
printf("%lld",sum);
}
int main(){
input();
work();
return 0;
}
链接:https://ac.nowcoder.com/acm/contest/3002/H
来源:牛客网
nozomi看到eli在字符串的“花园”里迷路了,决定也去研究字符串问题。
她想到了这样一个问题:
对于一个 “01”“01”串而言,每次操作可以把 00 字符改为 11 字符,或者把 11 字符改为 00 字符。所谓“01”“01”串,即只含字符 00 和字符 11 的字符串。
nozomi有最多 k 次操作的机会。她想在操作之后找出一个尽可能长的连续子串,这个子串上的所有字符都相同。
nozomi想问问聪明的你,这个子串的长度最大值是多少?
注:k次操作机会可以不全部用完。
K次机会一定是要么全部将0变成1,要么全部将1变成0,跟C题一样,我们也可以类比着维护一个长度为K的板子左右移动来找到最大值
#include
#include
#include
using namespace std;
int n,k;
const int maxn = 2e5 + 7;
int num[maxn];
int sum[maxn] = {0};
void input(){
scanf("%d%d",&n,&k);
for(int i = 0;i < n;i++){
scanf("%1d",&num[i]);
}
}
//将a变成b
int work(int a,int b){
//R维护右边界,L维护左边界
int L = 0,R = 0,ans = 1,change = 0;
for(int i = 0;i < n;i++){
if(num[i] == a){
if(change < k){
change++;
}else{
//寻找第一个a的位置,相当于是把板子向后移了一位
while(L <= R && num[L] != a){
L++;
}
L++;
}
}
R++;
ans = max(ans,R - L);
}
return ans;
}
int main(){
input();
int ans = max(work(1,0),work(0,1));
printf("%d",ans);
return 0;
}
链接:https://ac.nowcoder.com/acm/contest/3002/I
来源:牛客网
nico平时最喜欢说的口头禅是niconiconi~。
有一天nico在逛著名弹幕网站"niconico"的时候惊异的发现,n站上居然有很多她的鬼畜视频。其中有一个名为《让nico为你洗脑》的视频吸引了她的注意。
她点进去一看,就被洗脑了:“niconicoh0niconico*^vvniconicoG(vniconiconiconiconiconicoG(vniconico…”
弹幕中刚开始有很多“nico1 nico2”等计数菌,但到后面基本上都是“计数菌阵亡”的弹幕了。
nico也想当一回计数菌。她认为:“nico” 计a 分,“niconi” 计b分,“niconiconi” 计c分。
她拿到了一个长度为n的字符串,请帮她算出最大计数分数。
注:已被计数过的字符不能重复计数!如"niconico"要么当作"nico"+“nico"计 2a分,要么当作"niconi”+"co"计b 分。
吐槽一下精神污染题,我整个人都niconi了。。。看的时候有过DP的想法,但是感觉字符串处理自己不太会,就没有接着做下去,其实后来看来还算是比较简单的,直接dp就好,dp[n]维护最大值substring = "nico"的时候dp[i] = {dp[i],dp[i - 3] + a}
substring = "niconi"的时候dp[i]={dp[i],dp[i-5]+b}
subsring = "niconiconi"的时候dp[i] = {dp[i],dp[i-9]+b}
其实就是一道裸的DP题目。。。
#include
#include
#include
using namespace std;
int n,a,b,c;
const int maxn = 3e5 + 7;
char temp[maxn];
long long int dp[maxn] = {0};
void input(){
scanf("%d%d%d%d",&n,&a,&b,&c);
getchar();
for(int i = 0;i < n;i++){
temp[i] = getchar();
}
}
void work(){
for(int i = 1;i < n;i++){
if(i > 0){
dp[i] = dp[i - 1];
}
if(i >= 3 && temp[i - 3] == 'n' && temp[i - 2] == 'i' && temp[i - 1] == 'c'
&& temp[i] == 'o'){
dp[i] = max(dp[i - 1],dp[i - 3] + a);
}
if(i >= 5 && temp[i - 5] == 'n' && temp[i - 4] == 'i' && temp[i - 3] == 'c'
&& temp[i - 2] == 'o' && temp[i - 1] == 'n' && temp[i] == 'i'){
dp[i] = max(dp[i - 1],dp[i - 5] + b);
}
if(i >= 9 && temp[i - 9] == 'n' && temp[i - 8] == 'i' && temp[i - 7] == 'c'
&& temp[i - 6] == 'o' && temp[i - 5] == 'n' && temp[i - 4] == 'i'
&& temp[i - 3] == 'c' && temp[i - 2] == 'o' && temp[i - 1] == 'n'
&& temp[i] == 'i'){
dp[i] = max(dp[i],dp[i - 9] + c);
}
}
printf("%lld",dp[n - 1]);
}
int main(){
input();
work();
return 0;
}
链接:https://ac.nowcoder.com/acm/contest/3002/J
来源:牛客网
μ’s在九人齐心协力下,影响力越来越大了!
已知第一天影响力为x ,第二天影响力为y ,从第三天开始,每一天的影响力为前两天影响力的乘积再乘以a的 b 次方。 用数学语言描述是:
设第i天的影响力为f(i),那么f(i) = f(i - 2) * f(i - 1) * a ^b
她们想知道第天影响力是多少?
由于这个数可能非常大,只需要输出其对1e9+7取模的值就可以了
这道题目算是这一次,收获最多的一道题目了。
首先是费马小定理 如 果 p 是 一 个 质 数 , 而 a 不 是 p 的 倍 数 , a p − 1 ≡ 1 ( m o d p ) 如果p是一个质数,而a不是p的倍数,a^{p-1}\equiv 1(mod p) 如果p是一个质数,而a不是p的倍数,ap−1≡1(modp)
因此在这道题目里面,我们的幂指数只需要对1e9+6取模就行,a为1e9+7的倍数情况单独处理就好。我们意识到,f(n)我们可以用x,y,a来表示,x的幂次第一项是1,第二项是0,y的幂次项第一项是0,第二项是1,之后每一项均为前两项之和。a的幂次每一次均为前两项之和加1。结合求快速幂的算法,现在我们就就将问题放在了如何求斐波那契数列的第n个数上了。
矩阵快速幂求斐波那契数列的第i项
[ 0 1 1 1 ] ∗ [ f ( i ) f ( i + 1 ) ] = [ f ( i + 1 ) f ( i + 1 ) + f ( i ) ] = [ f ( i ) f ( i + 2 ) ] \left[\begin{matrix}0 & 1\\1 & 1\end{matrix}\right] *\left[\begin{matrix}f(i)\\f(i + 1)\\\end{matrix}\right] =\left[\begin{matrix}f(i + 1)\\f(i + 1) + f(i)\\\end{matrix}\right] = \left[\begin{matrix}f(i)\\f(i + 2)\\\end{matrix}\right] [0111]∗[f(i)f(i+1)]=[f(i+1)f(i+1)+f(i)]=[f(i)f(i+2)]
所以我们有
[ 0 1 1 1 ] n ∗ [ f ( 1 ) f ( 2 ) ] = [ f ( n + 1 ) f ( n + 2 ) ] \left[\begin{matrix}0 & 1\\1 & 1\end{matrix}\right]^{n} *\left[\begin{matrix}f(1)\\f(2)\\\end{matrix}\right] =\left[\begin{matrix}f(n + 1)\\f(n + 2)\\\end{matrix}\right] [0111]n∗[f(1)f(2)]=[f(n+1)f(n+2)]
对于x
[ 0 1 0 1 1 1 0 0 1 ] ∗ [ f ( i ) f ( i + 1 ) 1 ] = [ f ( i + 1 ) f ( i + 1 ) + f ( i ) + 1 1 ] = [ f ( i + 1 ) f ( i + 2 ) 1 ] \left[\begin{matrix}0 & 1 & 0\\1 & 1 & 1\\0 & 0 & 1\end{matrix}\right] *\left[\begin{matrix}f(i)\\f(i + 1)\\1\end{matrix}\right] =\left[\begin{matrix}f(i + 1)\\f(i + 1) + f(i) + 1\\1\\\end{matrix}\right] = \left[\begin{matrix}f(i + 1)\\f(i + 2)\\1\end{matrix}\right] ⎣⎡010110011⎦⎤∗⎣⎡f(i)f(i+1)1⎦⎤=⎣⎡f(i+1)f(i+1)+f(i)+11⎦⎤=⎣⎡f(i+1)f(i+2)1⎦⎤
[ 0 1 0 1 1 1 0 0 1 ] n − 3 ∗ [ 1 ( 即 f ( 3 ) ) 2 ( 即 f ( 4 ) ) 1 ] = [ f ( n ) f ( n + 1 ) 1 ] \left[\begin{matrix}0 & 1 & 0\\1 & 1 & 1\\0 & 0 & 1\end{matrix}\right]^{n-3}*\left[\begin{matrix}1(即f(3))\\2(即f(4))\\1\end{matrix}\right] = \left[\begin{matrix}f(n)\\f(n+1)\\1\end{matrix}\right] ⎣⎡010110011⎦⎤n−3∗⎣⎡1(即f(3))2(即f(4))1⎦⎤=⎣⎡f(n)f(n+1)1⎦⎤
同上,所以我只需要考虑求$ \left[
\begin{matrix}
0 & 1\
1 & 1
\end{matrix}
\right]^{n}$就可以了
方法就是用矩阵快速幂,详见代码部分
#include
#include
#define ll long long
using namespace std;
ll n,x,y,a,b;
const int base = 1e9 + 7;
int temp;
struct mt{
ll a[3][3];
};
void input(){
scanf("%lld%lld%lld%lld%lld",&n,&x,&y,&a,&b);
}
mt mulmt(mt a,mt b,ll mod){//计算矩阵乘法
mt res;
for(int i = 0;i < 3;i++){
for(int j = 0;j < 3;j++){
res.a[i][j] = 0;
for(int k = 0;k < 3;k++){
res.a[i][j] += a.a[i][k] * b.a[k][j] % mod;
res.a[i][j] %= mod;
}
}
}
return res;
}
mt power(mt a,ll b,ll mod){//计算矩阵快速幂
mt res;
for(int i = 0;i < 3;i++){
for(int j = 0;j < 3;j++){
res.a[i][j] = 0;
}
}
res.a[0][0] = res.a[1][1] = res.a[2][2] = 1;
while(b){
if(b & 1){
res = mulmt(res,a,mod);
}
b >>= 1;
a = mulmt(a,a,mod);
}
return res;
}
ll getFib(ll n,ll mod){//矩阵得到斐波那契数列的第n个值
mt temp;
for(int i = 0;i < 3;i++){
for(int j = 0;j < 3;j++){
temp.a[i][j] = 0;
}
}
temp.a[0][1] = temp.a[1][1] = temp.a[1][0] = temp.a[1][2] = 1;
mt res = power(temp,n - 1,mod);
return (res.a[0][0] + res.a[0][1]) % mod;
}
ll getFib2(ll n,ll mod){//得到第二个斐波那契数列的第n个值
mt temp;
for(int i = 0;i < 3;i++){
for(int j = 0;j < 3;j++){
temp.a[i][j] = 0;
}
}
temp.a[0][1] = temp.a[1][0] = temp.a[1][1] = temp.a[1][2] = temp.a[2][2] = 1;
mt res = power(temp,n - 1,mod);
return (res.a[0][0] + 2 * res.a[0][1] + res.a[0][2]) % mod;
}
ll quick(long long int a,long long int b){
long long int ans = 1;
long long int z = a;
while(b){
if(b & 1){
ans = ans * z % base;
}
z = z * z % base;
b >>= 1;
}
return ans % base;
}
void work(){
if(n == 1){
printf("%d",x % base);
}else if(n == 2){
printf("%d",y % base);
}else if(x % base == 0 || y % base == 0 || a % base == 0){
printf("0");
}else{
x %= base;
y %= base;
//由费马小定理我们可以知道,这里只需要对 base - 1取模就好
ll tempx = getFib(n - 2,base - 1);
ll tempy = getFib(n - 1,base - 1);
tempx = quick(x,tempx);
tempy = quick(y,tempy);
a = quick(a % base, b);
//之后的a = a^b % base
//我们是从第三项开始的,用第三项当成第一项,所以这里我们用的是n - 2
b = getFib2(n - 2, base - 1) % (base - 1);
a = quick(a % base,b);
cout<<tempx * tempy % base * a % base<<endl;
}
}
int main(){
input();
work();
return 0;
}