重庆还是挺冷的
Good Bye 2020 训练
演了一把 直接爆零好家伙
A题 Bovine Dilemma
没啥好说的,意思就是给你一个点(0,1) 剩下的n个点全在x轴上面,问你能够组成的不同面积的三角形有多少个 - - 既然高固定了,那么直接求一次不同的底有多少个就好了
B. Last minute enhancements
给了我们n个数,我们可以对一个数操作一次,要么删除它,要么让它+1,问最后有最多多少个不同的数。直接模拟吧
C. Canine poetry
大概意思是允许我们替换任意的字符,问最少多少次可以使得整个字符串不存在任何回文子串。首先考虑下大的回文串一定是小的回文串组成,最小回文串要么是长度为2,要么是长度为3的,那么我们直接破坏最小回文就OK ,枚举一次
D. 13th Labour of Heracles
根据度,放入一个容器,然后从大到小往先前的权值和加就OK
E. Apollo versus Pan
一个简单位运算的题吧,上面的式子显然可以变化一下
ΣiΣj (xi&xj)*Σk(xj|xk) 因为跟xj 挂钩 ,所以不妨变为ΣjΣi (xi&xj)*Σk(xj|xk)
枚举xj , 每次处理60位,就OK了。`
#include<bits/stdc++.h>
#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll long long
#define int long long
#define inf 0x3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
ll gcd(ll a,ll b){
if(a<0)a=-a;if(b<0)b=-b;return b==0?a:gcd(b,a%b);}
template<typename T>void read(T &res){
bool flag=false;char ch;while(!isdigit(ch=getchar()))(ch=='-')&&(flag=true);
for(res=ch-48;isdigit(ch=getchar());res=(res<<1)+(res<<3)+ch - 48);flag&&(res=-res);}
ll lcm(ll a,ll b){
return a*b/gcd(a,b);}
ll qp(ll a,ll b,ll mod){
ll ans=1;if(b==0){
return ans%mod;}while(b){
if(b%2==1){
b--;ans=ans*a%mod;}a=a*a%mod;b=b/2;}return ans%mod;}//快速幂%
ll qpn(ll a,ll b, ll p){
ll ans = 1;a%=p;while(b){
if(b&1){
ans = (ans*a)%p;--b;}a =(a*a)%p;b >>= 1;}return ans%p;}//逆元 (分子*qp(分母,mod-2,mod))%mod;
ll a[555555];
ll cnt[65];
ll pre[65];
signed main(){
ll t;
read(t);
for(int i=0;i<=60;i++){
pre[i]=qp(2,i,mods);
}
while(t--){
ll n;
read(n);
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++){
read(a[i]);
for(int j=0;j<60;j++){
if(a[i]>>j&1){
cnt[j]++; // 第j位有1
}
}
}
ll sum=0;
for(int i=1;i<=n;i++){
ll rnm=0;
ll cnm=0;
for(int j=0;j<60;j++){
if(a[i]>>j&1){
rnm=(rnm+pre[j]%mods*cnt[j]%mods)%mods;
cnm=(cnm+pre[j]%mods*n%mods)%mods ;
}
else{
// 如果此位无1
//& =0
cnm=(cnm+pre[j]%mods*cnt[j]%mods)%mods;
}
}
sum=(sum+cnm%mods*rnm%mods)%mods;
}
printf("%lld\n",sum%mods);
}
}
F题 待补 - - 下午刷专题
晚上搞了场context 来的有点晚 - -
A题直接模拟下就OK了 签到题
char s[111111];
char b[111111];
char op[10];
ll c[1111111];
ll cnt=0;
signed main(){
scanf("%s%s%s",s+1,b+1,op+1);
ll n=strlen(s+1);
ll m=strlen(b+1);
ll rnm=max(n,m)-min(n,m);
if(n>m){
for(int i=1;i<=rnm;i++){
ll ok=s[i]-'0';
if(op[1]=='a'){
// printf("%lld",ok&0);
c[++cnt]=ok&0;
}
if(op[1]=='o'){
c[++cnt]=ok|0;
}
if(op[1]=='x'){
c[++cnt]=ok^0;
}
}
for(int i=rnm+1,j=1;i<=n;i++,j++){
ll ok=s[i]-'0';
ll okk=b[j]-'0';
if(op[1]=='a'){
c[++cnt]=ok&okk;
}
if(op[1]=='o'){
c[++cnt]=ok|okk;
}
if(op[1]=='x'){
c[++cnt]=ok^okk;
}
}
}
if(n==m){
for(int i=1;i<=n;i++){
ll ok=s[i]-'0';
ll okk=b[i]-'0';
if(op[1]=='a'){
c[++cnt]=ok&okk;
}
if(op[1]=='o'){
c[++cnt]=ok|okk;
}
if(op[1]=='x'){
c[++cnt]=ok^okk;
}
}
}
if(n<m){
for(int i=1;i<=rnm;i++){
ll ok=b[i]-'0';
if(op[1]=='a'){
// printf("%lld",ok&0);
c[++cnt]=ok&0;
}
if(op[1]=='o'){
c[++cnt]=ok|0;
}
if(op[1]=='x'){
c[++cnt]=ok^0;
}
}
for(int i=rnm+1,j=1;i<=m;i++,j++){
ll ok=b[i]-'0';
ll okk=s[j]-'0';
if(op[1]=='a'){
c[++cnt]=ok&okk;
}
if(op[1]=='o'){
c[++cnt]=ok|okk;
}
if(op[1]=='x'){
c[++cnt]=ok^okk;
}
}
}
ll f=0;
for(int i=1;i<=cnt;i++){
if(f==0&&c[i]==0){
continue;}
f++;
printf("%lld",c[i]);
}
}
B题一个记忆化搜索搞定 注意点边缘细节
int ans;
int x,y,fx,fy,ax,ay,st=1e9;
int f[25][25];
int dx[4]= {
0,0,1,-1};
int dy[4]= {
1,-1,0,0};
ll dis(ll a,ll b,ll c,ll d)
{
return abs(a-c)+abs(b-d);
}
bool check(int x,int y)
{
return (x==1|| x==19||y==1 || y==19) && (x>=1 && x<=19 && y>=1 && y<=19);
}
bool canget(int tx,int ty)
{
return tx==fx || ty==fy;
}
void dfs(int lx,int ly,int sum)
{
ll nx,ny;
if (sum>=f[lx][ly]){
return ;}
f[lx][ly]=sum;
if (canget(lx,ly) && dis(lx,ly,fx,fy)<ans)
ans=dis(x,y,fx,fy),st=sum+dis(x,y,fx,fy);
if (canget(lx,ly) && dis(lx,ly,fx,fy)==ans)
st=min(st,st=sum+dis(x,y,fx,fy));
for (int i=0; i<4; ++i)
{
nx=lx+dx[i];
ny=ly+dy[i];
if (check(nx,ny)==false)
continue;
dfs(nx,ny,sum+1);
}
return;
}
signed main()
{
memset(f,inf,sizeof(f));
read(x);
read(y);
read(fx);
read(fy);
dfs(x,y,0);
printf("%lld\n",f[fx][fy]);
return 0;
}
因为冠军只跟接下来一轮的排名挂钩 我们先对之前的得分排个序 从大到小,前面的得分越高,我们现在给它分配越低的分数,对于其他前面分数不高的选手就有机会取到冠军,记录pre[i] 表示1–i 区间的最大值,suf[i] 表示n–i区间的最大值,现在枚举a[i] 令它为第一名,也就是a[i]+n 那么如果对于前缀pre[i] 满足 a[i]+n>=pre[i] 并且 a[i]+n>=suf[i]-1 (因为后面的区间分配了一个n,现在n并不在后面的区间了) 那么说明这个人可以成为冠军
const int ma=3e5+7;
ll pre[ma];
ll suf[ma];
ll a[ma];
ll sum[ma];
bool cmp(ll x,ll y){
return x>y;
}
signed main(){
ll n;
read(n);
for(int i=1;i<=n;i++){
read(a[i]);
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
sum[i]=i+a[i]; // ai>=ai-1 +1
pre[i]=max(pre[i-1],sum[i]);
}
for(int i=n;i>=1;i--){
suf[i]=max(suf[i+1],sum[i]);
}
ll ans=0;
for(int i=1;i<=n;i++){
if(a[i]+n>=pre[i]&&a[i]+n>=suf[i]-1){
ans++;//n 已经被拿了
}
}
printf("%lld",ans);
}
n 1e5 联想到了昨天写过的那个单调队列+dp 这个题想了很久 ,正向我实在是推不走, 于是逆向dp
记dp[i][0/1] 表示在n–i 这个区间内 (0)先手 (1)后手的最佳情况
转移方程见代码吧 每次要控制在m的区间内
const int maxn=1e5+10;
ll a[maxn],dp[maxn][3];
ll que[maxn],pue[maxn];
signed main(){
ll n,m;
read(n);
read(m);
for(int i=1; i<=n; i++)
{
read(a[i]);
}
que[1]=n;
pue[1]=n;
ll head=1;
ll tail=1;
ll head_=1;
ll tail_=1;
for(int i=n; i>=1; i--)
{
while(head<=tail&&dp[que[tail]+1][1]+a[que[tail]]<=dp[i+1][1]+a[i]) // 上个状态 n-- i+1 我的最优
tail--; //否则出去
que[++tail]=i;
while(head_<=tail_&&dp[pue[tail_]+1][0]-a[pue[tail_]]>=dp[i+1][0]-a[i]) //同理
tail_--;
pue[++tail_]=i;
while(head<=tail&&que[head]>=i+m)
head++;
while(head_<=tail_&&pue[head_]>=i+m)
head_++;
dp[i][0]=dp[que[head]+1][1]+a[que[head]];
dp[i][1]=dp[pue[head_]+1][0]-a[pue[head_]];
}
printf("%lld\n%lld",dp[1][0],dp[1][1]);
return 0;
}