题目链接:https://www.jisuanke.com/contest/1557?view=challenges
A题
题意:给你2^k个面具,然后n个人,要求挨着的2个人二进制之后不能互补。问你有多少不同的方案数
思想:对于这2^k个数最多就两个数是互补的,所以考虑下第一个数有2^k种选择,第二个2^k-1种,假如倒数第二个跟第一个不一样,那么最后一个由2^k-2种选择,假如一样的话,那么就可以将序列缩短到n-2,继续递归下去计算。
Ps:对于第一个题,可以推那个容斥的公式,或者看下这个DP写的非常好:http://www.cnblogs.com/565261641-fzh/p/9616556.html
#include
using namespace std;
typedef long long ll;
const ll mod =1e9+7;
const int maxn=1e6+5;
ll a[maxn];
void init()
{
a[0]=1;
for(int i=1;i>1;
}
return ans%mod;
}
ll check(ll n,ll k)
{
if(n==1)//只有一个人
return a[k]%mod;
else if(n==2)//只有两个人
return (a[k]*(a[k]-1))%mod;
ll ans;
ans=((a[k]*Pow((a[k]-1),n-2)%mod)*(max(a[k]-2ll,0ll)))%mod;
ans=(ans+check(n-2,k)%mod)%mod;
return ans;
}
int main()
{
init();
int t;
scanf("%d",&t);
while(t--)
{
ll n,k;
scanf("%lld%lld",&n,&k);
ll T=check(n,k);
printf("%lld\n",T);
}
return 0;
}
B题
题意:两个人玩游戏,有n个轮次,初始分数为m,每个轮次有三种选择,A:将m增加a,B:将m减少b,C:可以将m取 相反数。只能选择一个对m进行操作。K玩家想要使最终分数大于等于k,S玩家想要使最终分数小于等于l,K玩家 先手,问最终K玩家能否使分数大于等于k。
思想:记忆化搜索(玄学时间复杂度),先将m加100,这样就不用考虑负数的情况了,但是判断还得需要减去100.
#include
using namespace std;
const int maxn = 1e3+5;
int n,m,k,l;
int dp[maxn][205];//将复数的放上边
int a[maxn];
int b[maxn];
int c[maxn];
int dfs(int id,int num)
{
if(id>n)
return num-100;
if(dp[id][num]!=(1<<29))
return dp[id][num];
if(id%2==1)//因为从1开始 所以第一个是max
{
int temp=-100;//寻找这个操作的最大值
if(a[id])//对于每种可能都去搜索一遍
{
int Temp=min((int)100,num-100+a[id]);//num加了100 所以需要减去100
temp=max(temp,dfs(id+1,Temp+100));
}
if(b[id])
{
int Temp=max((int)-100,num-100-b[id]);
temp=max(temp,dfs(id+1,Temp+100));
}
if(c[id])
{
int Temp=num-100;
temp=max(temp,dfs(id+1,-Temp+100));
}
dp[id][num]=temp;
return temp;
}
else//min
{
int temp=100;//寻找这个可能的最小值
if(a[id])
{
int Temp=min((int)100,num-100+a[id]);
temp=min(temp,dfs(id+1,Temp+100));
}
if(b[id])
{
int Temp=max((int)-100,num-100-b[id]);
temp=min(temp,dfs(id+1,Temp+100));
}
if(c[id])
{
int Temp=num-100;
temp=min(temp,dfs(id+1,-Temp+100));
}
dp[id][num]=temp;
return temp;
}
}
int main()
{
for(int i=0;i=k)
printf("Good Ending\n");
else if(temp<=l)
printf("Bad Ending\n");
else
printf("Normal Ending\n");
return 0;
}
F题
题意:给你n帧画面,问你最长的连续点的长度是多少,就是对于i中有(1,1)下一秒还有 那么长度就2,也就说答案要不是0要不就大于2;
思想 map搞一下,模拟那个过程即可。
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
struct point{
int x, y;
}a[100005];
int ans[100005];
map , int>mp, mpp;
int main()
{
int T, n, c, maxx;
scanf("%d", &T);
while(T--){
mp.clear();
scanf("%d", &n);
maxx = 0;
for(int i = 0; i < n; i++){
scanf("%d", &c);
for(int j = 0; j < c; j++){
scanf("%d%d", &a[j].x, &a[j].y);
pair p(a[j].x, a[j].y);
if(!mpp[p]){
mpp[p] = 1;
mp[p]++;
}
int temp = mp[p];
ans[j] = temp;
if(temp > maxx)
maxx = temp;
}
mpp.clear();
mp.clear();
for(int j = 0; j < c; j++){
pair p(a[j].x, a[j].y);
mp[p] = ans[j];
}
}
if(maxx > 1)
printf("%d\n", maxx);
else
printf("0\n");
}
return 0;
}
G题
题意:就是给你n次冲浪形成的一个矩阵,每次都会留下一个痕迹,但是也有可能会毁掉原来留下的痕迹,问你痕迹的长度是多少。
思想:两个树状数组,维护X和Y的最大值,从后往前,因为前边有可能被后边的干掉。发现对于某次海浪,改变的值等于比当前X大的的所有X里面的Y最大值和当前Y的差值,X同理。
#include
using namespace std;
typedef long long ll;
const int maxn=50005;
ll x[maxn];
ll y[maxn];
ll Qx[maxn*200];
ll Qy[maxn*200];
void add(int x,ll valu)
{
for(ll i=x;i>0;i-=(i&(-i)))
Qy[i]=max(Qy[i],valu);
}
void Add(int x,ll valu)
{
for(ll i=x;i>0;i-=(i&(-i)))
Qx[i]=max(Qx[i],valu);
}
ll query(ll x)
{
ll ans=0;
for(ll i=x;i=0;i--)
{
ll temp=query(x[i]);//比X[i]大的最大的y
sum=sum+y[i]-temp;
temp=Query(y[i]);//比y[i]大的最大的x
sum=sum+x[i]-temp;
add(x[i],y[i]);
Add(y[i],x[i]);
// printf("%lld\n",sum);
}
printf("%lld\n",sum);
return 0;
}
H题
题意:有n个数,q次询问,每次不是问区间那个式子的值,要不就是改变某点的值。
思想:树状数组,一个建立a[i] 一个建立i*a[i] ,维护即可,为什么这样 自己画画,就OK了
#include
using namespace std;
typedef long long ll;
const int Maxn=1e5+5;
ll a[Maxn];
ll b[Maxn];
ll c[Maxn];
int n,q;
void add(int x,ll num)
{
while(x<=n)
{
b[x]+=num;
x+=(x&(-x));
}
}
void Add(int x,ll num)
{
while(x<=n)
{
c[x]+=num;
x+=(x&(-x));
}
}
ll query(int x)
{
ll ans=0;
while(x)
{
ans+=b[x];
x-=(x&(-x));
}
return ans;
}
ll Query(int x)
{
ll ans=0;
while(x)
{
ans+=c[x];
x-=(x&(-x));
}
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
add(i,a[i]);
Add(i,i*a[i]);
}
while(q--)
{
int op,x;
ll y;
scanf("%d%d%lld",&op,&x,&y);
if(op==1)
{
ll temp=(y+1)*(query(y)-query(x-1));
ll Temp=temp-(Query(y)-Query(x-1));
printf("%lld\n",Temp);
}
else
{
add(x,-a[x]);
Add(x,-x*a[x]);
a[x]=y;
add(x,a[x]);
Add(x,x*a[x]);
}
}
return 0;
}
I题
题意:给你个单词和字符串,对于字符串的每个单词可以通过已给的方式得到一个2位数,然后拼接len*2个数字,去掉前导0多长。
思想:模拟
#include
using namespace std;
char str[1000005];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,ans=0;
char c;
scanf("%d %c",&n,&c);
scanf("%s",str);
int len=strlen(str);
vectorV;
for(int i=0;i
J题
题意:给你一个n*m的格子,每个点可以往右和往下连边,给你对应的连边的权值,问你最少建立多少花费的边使所有点之间都可以互相达到,并且保证路径是唯一的,问你这样花费最小是多少。
思想:正着想不好想,所以就反过来想,怎么花费最小,就是让构成一个树花费最大即可,就变成了求最大生成树,然后树上两点的距离就变成了求LCA即可。
#include
using namespace std;
typedef long long ll;
const int maxn= 260005;
int n,m,cnt;
int head[maxn];
int fa[maxn];
int dep[maxn];
ll dist[maxn];
int pre[maxn][30];
struct node{
int u;
int v;
ll va;
bool operator < (const node &a)
{
return va > a.va;
}
}no[maxn<<1];
struct Node{
int v;
int next;
ll valu;
}No[maxn<<2];
void add(int u,int v,ll w)
{
No[cnt].v=v;
No[cnt].valu=w;
No[cnt].next=head[u];
head[u]=cnt++;
}
int _id(int x,int y)
{
return (x-1)*m+y;
}
int find(int x)
{
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
void dfs(int u,int fa,int deep)
{
dep[u]=deep;
pre[u][0]=fa;
for(int i=head[u];i!=-1;i=No[i].next)
{
int v=No[i].v;
if(v==fa)
continue;
if(dist[v]==0)
{
dist[v]=dist[u]+No[i].valu;
dfs(v,u,deep+1);
}
}
}
void __ST()
{
for(int j=1;(1<= 0;i--)
{
if(dep[x] - (1<= dep[y])
x = pre[x][i];
}
if(x == y)
return x;
for(int i = temp;i >= 0;i--)
{
if( pre[x][i] != pre[y][i])
{
x = pre[x][i];
y = pre[y][i];
}
}
return pre[x][0];
}
ll Getdist(int x,int y)
{
return dist[x]+dist[y]-2*dist[lca(x,y)];
}
int main()
{
char a[5],b[5];
ll c,d;
cnt=0;
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=0;i<=n*m;i++)
fa[i]=i;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%s%lld%s%lld",a,&c,b,&d);
no[cnt++]=node{_id(i,j),_id(i+1,j),c};
no[cnt++]=node{_id(i,j),_id(i,j+1),d};
}
}
sort(no,no+cnt);
int Count=0;
for(int i=0;i