有 n n n 个鱼塘,每个鱼塘是四种鱼塘中的一种,这四种分别是啥都没、有鱼饵、有鱼、都有,每次可以选择四种操作之一:不动、拿鱼、拿鱼饵、用鱼饵钓鱼(可以在没鱼的鱼塘里钓),求最后的最大鱼数。
有饵没鱼的肯定拿饵,有鱼没饵的肯定拿鱼,啥都没有的肯定尝试钓鱼,都有的肯定拿鱼,因为不知道鱼饵后面能不能有机会钓。
然后后面还剩了 k k k 个饵,可以考虑只要前 k 2 \dfrac k 2 2k 个,在遇到后面的 k 2 \dfrac k 2 2k 个饵时,由于是多余的,就不拿了,操作换成钓鱼。
代码如下:
#include
int T,n;char s[2000010];
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d %s",&n,s+1);
int sum1=0,sum2=0;
for(int i=1;i<=n;i++)
switch(s[i])
{
case '0':
if(sum1)sum1--,sum2++;
break;
case '1':
sum1++;
break;
case '2':
sum2++;
break;
case '3':
sum2++;
break;
}
printf("%d\n",sum2+sum1/2);
}
}
给出一个字符串,有三种操作,1、将前 k k k 个字符移到后面,2、将后 k k k 个字符移到前面,3、询问第 k k k 个字符是谁。
前两个操作其实可以看成字符串的整体左移和右移,所以记录一下字符串的开头在哪里可以搞询问了。
代码如下:
#include
#include
char s[2000010];
int n,m;
int main()
{
scanf("%s",s+1);n=strlen(s+1);
scanf("%d",&m);
char id[2];int x,now=1;while(m--)
{
scanf("%s %d",id,&x);
if(id[0]=='M'){
now-=x;
if(now<1)now+=n;
if(now>n)now-=n;
}else{
x=x-now+1;
if(x<1)x+=n;
printf("%c\n",s[x]);
}
}
}
给出一个手掌,问这是右手还是左手。
我的做法是先找到手掌底部的两个点,只有他们的距离是 9 9 9,很好找,分别记为第 i i i 和 i + 1 i+1 i+1 个点。
然后再判断一下第 i − 1 i-1 i−1 个点与 i i i 的距离,看看是 6 6 6 还是 8 8 8,这样能判断大拇指位置。
然后再用叉积判一下点是顺时针给的还是逆时针给的即可。
代码如下:
#include
#include
#define eps 0.1
int T;
struct point{
double x,y;
double det(point B){return x*B.y-y*B.x;}
point operator -(const point B){return (point){x-B.x,y-B.y};}
}a[21];
int pre(int x){return x==1?20:x-1;}
int next(int x){return x==20?1:x+1;}
double dis(point x,point y){return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));}
int main()
{
scanf("%d",&T);while(T--)
{
for(int i=1;i<=20;i++)scanf("%lf %lf",&a[i].x,&a[i].y);
for(int i=1;i<=20;i++)if(fabs(dis(a[i],a[next(i)])-9.0)<eps){
double x=dis(a[i],a[pre(i)]);int ans;
if(fabs(x-6.0)<eps)ans=1;else ans=0;
if((a[next(i)]-a[i]).det(a[next(next(i))]-a[i])<0)ans^=1;
if(ans==1)printf("right\n");else printf("left\n");
break;
}
}
}
要求将平面上 n n n 个点染成黑色,其他都是白色,存在恰好有 m m m 对点 (X,Y),满足点 X , Y X,Y X,Y 相邻,且一个为黑一个为白。
考虑将这 n n n 个点一行放 n \sqrt n n 个堆积起来,此时的答案是最小的。
然后逐渐将最上面那行的最右边那个放到最下面那行的最右边,每次移动一个可以使答案 + 2 +2 +2,直到所有点都在一条直线上并且都相邻。
然后再从左到右考虑第 i i i 个点,将第 i i i ~ n n n 个点整体右移一位,即让 i i i 和 i − 1 i-1 i−1 中间空出一位,这样又可以使答案 + 2 +2 +2。
在做上面这个过程时,当答案等于 m m m 时,输出即可。
代码如下:
#include
#include
#include
#include
using namespace std;
#define maxn 60
int T,n,m,ans;
struct point{int x,y;}s[maxn];
bool v[maxn][maxn];
void add(int x,int y)
{
if(v[x-1][y])ans--;else ans++;
if(v[x+1][y])ans--;else ans++;
if(v[x][y-1])ans--;else ans++;
if(v[x][y+1])ans--;else ans++;
v[x][y]=true;
}
void print()
{
printf("Yes\n");
for(int i=1;i<=n;i++)printf("%d %d\n",s[i].x,s[i].y);
}
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d %d",&n,&m);ans=0;
if(m%2){printf("No\n");continue;}
memset(v,false,sizeof(v));
int len=sqrt(n),x=1,y=1;
for(int i=1;i<=n;i++)
{
s[i]=(point){x,y};add(x,y);
y++;if(y>len)x++,y=1;
}
if(m<ans||m>4*n){printf("No\n");continue;}
if(m==ans){print();continue;}
x=1,y=len+1;
for(int i=n;i>len;i--)
{
if(s[i].y!=1)ans+=2;
s[i]=(point){x,y};y++;
if(ans==m)break;
}
if(ans==m){print();continue;}
x=1,y=1;
for(int i=1;i<=n;i++){
s[i]=(point){x,y};y++;
if(ans<m)y++,ans+=2;
}
print();
}
}
称 1 1 1 ~ n n n 的一个排列 p p p 是“配对的”当且仅当 ∀ i , p i ≠ i , p p i = i \forall i,p_i\neq i,p_{p_i}=i ∀i,pi=i,ppi=i,现在有一个长度为 n n n 的序列 A A A,定义一个排列的代价为 ∑ i = 1 n ∣ a i − a p i ∣ / 2 \sum_{i=1}^n |a_i-a_{p_i}|/2 ∑i=1n∣ai−api∣/2,现在要找两个完全不同的“配对的”排列使得总代价最小。
“配对的”排列其实就是将 1 1 1 ~ n n n 这个排列每一位都与其他位进行交换,且每一位仅交换一次,如 3 , 4 , 1 , 2 3,4,1,2 3,4,1,2 就是一个“配对的”排列, 1 1 1 与 3 3 3 交换了, 2 2 2 与 4 4 4 交换了,下面称交换的两位是相互配对的。
那么一个排列的代价其实就是将每个配对的 A i A_i Ai 之差加起来,那么显然最小的一个方案就是将 A A A 进行排序,然后相邻的两个进行配对。
考虑第二小的方案,由于两个方案不能有任何一个配对相同,所以此时只能考虑相邻的 4 4 4 个进行相互配对和相邻 6 6 6 个进行相互配对。相邻 8 8 8 个以上的配对就不需要考虑了,因为他们拆成 4 4 4 个和 6 6 6 个的一定不会更劣,然后做一次dp即可。
相邻 4 4 4 个的最优配对方案为 ( 1 , 3 ) , ( 2 , 4 ) (1,3),(2,4) (1,3),(2,4),相邻 6 6 6 个的最优配对方案为 ( 1 , 3 ) , ( 2 , 5 ) , ( 4 , 6 ) (1,3),(2,5),(4,6) (1,3),(2,5),(4,6)。
代码如下:
#include
#include
#include
using namespace std;
#define maxn 200010
#define ll long long
int T,n,a[maxn];
ll f[maxn],ans;
ll calc1(int x){return a[x+2]-a[x]+a[x+3]-a[x+1];}
ll calc2(int x){return a[x+2]-a[x]+a[x+5]-a[x+3]+a[x+4]-a[x+1];}
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),f[i]=2147483647;
sort(a+1,a+n+1);ans=0;
for(int i=1;i<=n;i+=2)ans+=a[i+1]-a[i];
for(int i=0;i<=n;i++){
if(i+4<=n)f[i+4]=min(f[i+4],f[i]+calc1(i+1));
if(i+6<=n)f[i+6]=min(f[i+6],f[i]+calc2(i+1));
}
printf("%lld\n",ans+f[n]);
}
}
找两个分数 c d , e f \dfrac c d,\dfrac e f dc,fe,满足 c d − e f = a b \dfrac c d-\dfrac e f=\dfrac a b dc−fe=ba,且 d , f < b d,fd,f<b。
先将 a , b a,b a,b 化简为 a ′ , b ′ a',b' a′,b′,然后转化一下上面的柿子:
c d − e f = a ′ b ′ c f − e d d f = a ′ b ′ \frac c d-\frac e f=\frac {a'} {b'}\\ \dfrac {cf-ed} {df}=\frac {a'} {b'} dc−fe=b′a′dfcf−ed=b′a′
下面不妨令 a ′ = c f − e d , b ′ = d f a'=cf-ed,b'=df a′=cf−ed,b′=df。
假如 b ′ < b b'b′<b,那么存在一组解 c = a ′ + b ′ , d = b ′ , f = 1 , e = 1 c=a'+b',d=b',f=1,e=1 c=a′+b′,d=b′,f=1,e=1。
如果 b ′ = b b'=b b′=b,那么 d d d 就不能等于 b ′ b' b′ 了,因为 d d d 要小于 b b b。
考虑枚举 d , f d,f d,f,因为要让 c f − e d = a ′ cf-ed=a' cf−ed=a′ 有解,所以 gcd ( d , f ) ∣ a ′ \gcd(d,f)|a' gcd(d,f)∣a′,而 gcd ( a ′ , b ′ ) = 1 \gcd(a',b')=1 gcd(a′,b′)=1,所以 gcd ( d , f ) = 1 \gcd(d,f)=1 gcd(d,f)=1。
枚举出 d , f d,f d,f 后,直接扩欧求 c , e c,e c,e 即可。
代码如下:
#include
#include
#include
using namespace std;
#define ll long long
ll gcd(ll x,ll y){return !y?x:gcd(y,x%y);}
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b){x=1;y=0;return;}
exgcd(b,a%b,y,x);y-=a/b*x;
}
ll T,a,b,c,d,e,f;
int main()
{
scanf("%lld",&T);while(T--)
{
scanf("%lld %lld",&a,&b);d=-1;
ll p=gcd(a,b);if(p>1){printf("%lld %lld 1 1\n",a/p+b/p,b/p);continue;}
for(ll i=2;i*i<=b;i++)if(b%i==0&&gcd(i,b/i)==1){d=i,f=b/i;break;}
if(d==-1){printf("-1 -1 -1 -1\n");continue;}
exgcd(f,d,c,e);e=-e;
while(c<=0||e<=0)c+=d,e+=f;c*=a;e*=a;
printf("%lld %lld %lld %lld\n",c,d,e,f);
}
}
你有 n + 1 n+1 n+1 个长度为 n n n 的字符串, s 0 s_0 s0 的第 i i i 位是数字 i m o d 10 i\bmod 10 imod10, s i ( i > 0 ) s_i(i>0) si(i>0) 由 s i − 1 s_{i-1} si−1 将第 p i − 1 p_{i-1} pi−1 位的数字换成 d i − 1 d_{i-1} di−1 得到,保证 p p p 是 0 0 0 ~ p − 1 p-1 p−1 的一个排列,现在要你将 s 0 s_0 s0 ~ s n s_n sn 进行排序,输出它们的排名。
由于 p p p 是 0 0 0 ~ p − 1 p-1 p−1 的一个排列,说明每个位置只会被替换一次,而由于是按字典序排序,所以在前面的位会比后面的位影响要大,当更改了 0 0 0 位置后,就可以确定更改前的串与更改后的串的大小关系,所以可以考虑分治,每次处理区间内最前面的位置。
求区间最值可以用笛卡尔树,代码如下:
#include
#include
#include
using namespace std;
#define maxn 2000010
#define inf 999999999
int T,n,seed,A,B,MOD;
int p[maxn],d[maxn],rk[maxn],id=0;
int zhan[maxn],t,ls[maxn],rs[maxn];
void build(){
zhan[t=1]=0;
for(int i=1;i<n;i++){
while(t&&p[zhan[t]]>p[i])t--;
if(t)ls[i]=rs[zhan[t]],rs[zhan[t]]=i;
else ls[i]=zhan[1];
zhan[++t]=i;
}
}
void solve(int l,int r,int pos){
if(l>=r||p[pos]==inf){for(int i=l;i<=r;i++)rk[i]=id++;return;}
if(p[pos]%10>d[pos])solve(pos+1,r,rs[pos]),solve(l,pos,ls[pos]);
else solve(l,pos,ls[pos]),solve(pos+1,r,rs[pos]);
}
#define mod 1000000007
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d",&n);
scanf("%d %d %d %d",&seed,&A,&B,&MOD);
for(int i=0;i<n;i++)p[i]=i;
for(int i=1;i<n;i++){
swap(p[seed%(i+1)],p[i]);
seed=(1ll*seed*A+B)%MOD;
}
scanf("%d %d %d %d",&seed,&A,&B,&MOD);
for(int i=0;i<n;i++){
d[i]=seed%10;
seed=(1ll*seed*A+B)%MOD;
}
for(int i=0;i<n;i++)if(p[i]%10==d[i])p[i]=inf;
build();id=0;solve(0,n,zhan[1]);
int ans=0;for(int i=0;i<=n;i++)ans=(ans+1ll*rk[i]*ksm(10000019,i)%mod)%mod;
printf("%d\n",ans);
}
}
判断一个字符串开头是否为 lovely,不在乎大小写。
送分题,代码如下:
#include
char s[1000010];
int main()
{
scanf("%s",s+1);
for(int i=1;i<=6;i++)if(s[i]>='A'&&s[i]<='Z')s[i]=s[i]-'A'+'a';
if(s[1]=='l'&&s[2]=='o'&&s[3]=='v'&&s[4]=='e'&&s[5]=='l'&&s[6]=='y')printf("lovely");
else printf("ugly");
}