T3
题面
野生皮卡丘
时间限制:1秒 内存限制:128M
题目描述
脸颊两边有着小小的电力袋。遇到危险时就会放电。会将尾巴竖起来,去感觉周围是否安全。没错这个就是皮卡丘,野生的皮卡丘大多群体行动,而且很少固定在一个地方。
在皮卡丘的家族中,有 n 个房间,每个房间中有0~4只皮卡丘,且房间中最多居住4只皮卡丘,由于皮卡丘喜欢热闹,所以当某个房间中只有1只或2只皮卡丘时,皮卡丘就会感到孤独,所以要进行调整,协调皮卡丘换房间,最少让多少皮卡丘换房间才能使得所有的皮卡丘都不会感到孤独,如果无法做到请输出-1。
输入描述
第一行:一个数n,表示有n个房间
第二行:n个数,第个数表示第个房间中有多少只皮卡丘 [0,4]
30%的数据:0≤ n≤ 100
100%的数据:0≤ n≤ 10^6
输出描述
一个数ans,ans只皮卡丘换房间后使得所有房间的皮卡丘都不会感到孤独,若没有可以满足的情况输出-1
样例
输入
5 2 2 1 3 4
输出
2
初次思路
越想越复杂,直接开摆
复盘思路
运用贪心,按投入次数/解决的数量一次解决
图例:
=:收益
->:吧……放到……中
基本思路
1->2//1=2
剩下1,2没了
(1,1)->1//2=3
还剩下1
有3
1->3//1=1
无3
4->1//2=1
剩下2,1没了
2->(2,2)//2=3
有4
4->2//1=1
无4
2->(3,3)//2=1
2->2
code
#include<bits/stdc++.h>
using namespace std;
int n,a[1000005],num[10];
int main(){
//freopen("room.in","r",stdin);
//freopen("room.out","w",stdout);
while(cin>>n){
int ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
num[a[i]]++;
}
if(num[1]>num[2]){
ans+=num[2];
num[1]-=num[2];
num[2]=0;
ans+=num[1]/3;
num[1]%=3;
if(num[1]==1){
num[1]=0;
if(num[3]>0){
ans+=1;
}
else if(num[4]>1){
ans+=2;
}
else{
num[1]=1;
}
}
else if(num[1]==2){
num[1]=0;
if(num[3]>1){
ans+=1;
}
else if(num[3]>0){
ans+=1;
if(num[4]>1){
ans+=2;
}
else{
num[1]=1;
}
}
else{
num[1]=1;
}
}
/*if(num[1]>0){
if(num[3]>0){
if(num[3]>=num[1]){
ans+=num[1];
num[1]=0;
}
else{
ans+=num[3];
num[1]-=num[3];
num[4]+=num[3];
if(num[4]/2>=num[1]){
ans+=2*num[1];
num[1]=0;
}
}
}
else{
if(num[4]/2>=num[1]){
ans+=2*num[1];
num[1]=0;
}
}
}*/
}
else if(num[1]<num[2]){
ans+=num[1];
num[2]-=num[1];
ans+=(num[2]/3);
num[2]%=3;
num[1]=0;
if(num[2]==1){
num[2]=0;
if(num[4]>0){
ans+=1;
}
else if(num[3]>1){
ans+=2;
}
else{
num[2]=1;
}
}
else if(num[2]==2){
ans+=2;
num[2]=0;
}
/*if(num[4]>0){
if(num[4]>=num[2]){
ans+=num[2];
num[2]=0;
}
else{
ans+=num[4];
num[2]-=num[4];
num[3]+=2*num[4];
if(num[3]/2>=num[2]){
ans+=2*num[2];
num[2]=0;
}
}
}
else{
if(num[3]/2>=num[2]){
ans+=2*num[2];
num[2]=0;
}
}*/
}
else{
ans+=num[1];
num[1]=0;
num[2]=0;
}
if(num[1]>0||num[2]>0){
cout<<-1<<'\n';
}
else{
cout<<ans<<'\n';
}
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
/*
1->2//1=2
剩下1,2没了
(1,1)->1//2=3
还剩下1
有3
1->3//1=1
无3
4->1//2=1
剩下2,1没了
2->(2,2)//2=3
有4
4->2//1=1
无4
2->(3,3)//2=1
2->2
5
2 2 1 3 4
3
1 2 3
*/
T1
题面
树上博弈
时间限制:2秒 内存限制:256M
题目描述
小可和达达喜欢玩♂游戏。
他们再一棵树上玩游戏,已知,树有n个点,编号从 1~n,每条边都有一个正整数边权wi。
小可和达达会在这棵树上玩很多次游戏。每次游戏开始前,丫丫都会给定一个点对 (u,v),
u和v是树上不同的两个点,然后把树上 u 到 v 的路径上所有边的边权拿出来,生成一个数组 s。
小可和达达轮流在 s 上以最优策略进行游戏,小可为先手。
游戏规则是这样的:
1.小可第一次取走一个数字。
2.记上一个人取走的数字是 x,当前的人需要从 s 中取走一个不大于 x 的数。
3.如果不能取数,则为输。
丫丫觉得这个游戏十分地有趣,她想知道,在所有可能的初始点对 (u,v) 中, 有多少种情况能使小可有必胜策略。
输入描述
第一行:输入一个正整数T,表示多组输入组数。
对于每一组数据:描述一棵树:
第一行:输入一个正整数 n,表示树的大小。
接下来 n-1 行,每行输入三个数 u,v,w,代表树上有一条权值为 w 的边,连接u,v两个点。
输出描述
对于每组数据,输出一行一个数,表示有多少种初始点对能使小可有必胜策略。
输入样例1
3
5
1 2 2
1 3 1
3 4 1
3 5 2
5
1 2 0
2 3 2
3 4 2
4 5 0
5
1 2 0
1 3 1
3 4 0
3 5 2
输出样例1
9
8
10
样例解释1
对于第一组数据,树的形态如下:
一共10中选点对的方案。
当选择(1,4)时,
s={1,1};
小可取1,达达取1
小可不能再取数。达达获胜。
其他的情况,小可都有必胜策略,答案为9。
数据描述
27%的数据:n≤5000
100%的数据:1≤T≤10,1≤∑n≤5<em>10e5,1≤u,v≤n≤5</em>10e5,0≤w≤10e9
初次思路
啊?
复盘思路
数列最小值有奇数个必赢
↓
数列最小值有偶数个,数列次小值有奇数个,必赢
↓
有数出现奇数次,必赢
↓
数列所有数异或,不为0,必赢
大概思路就是这样,但要注意某些特殊情况,会使不赢的数列异或为0(如01,11,10),因此我们将权值替换为较大映射值
code
#include<bits/stdc++.h>
using namespace std;
int t,n;
long long dis[500005],base[500005],ans;
vector<pair<int,int> >g[500005];
map<long long,long long>mp,mpt;
void dfs(int u,int pre){
for(int i=0;i<g[u].size();i++){
if(g[u][i].first!=pre){
dis[u]=g[u][i].second^dis[u];
dfs(g[u][i].first,u);
}
}
}
int main(){
//freopen("game.in","r",stdin);
//freopen("game.out","w",stdout);
scanf("%d",&t);
base[0]=1;
for(int i=1;i<=500000;i++){
base[i]=base[i-1]*13331;
}
while(t--){
mp.clear();
mpt.clear();
memset(g,0,sizeof g);
scanf("%d",&n);
int u,v,w;
for(int i=1;i<=n;i++){
scanf("%d%d%d",&u,&v,&w);
if(mp[w]==0){
mp[w]=base[i];
}
g[u].push_back(make_pair(v,mp[w]) );
g[v].push_back(make_pair(u,mp[w]) );
}
dfs(1,0);
ans=n*(n-1)/2;
for(int i=1;i<=n;i++){
ans-=mpt[dis[i]];
mpt[dis[i]]++;
}
cout<<ans<<'\n';
}
fclose(stdin);
fclose(stdout);
return 0;
}
//效率是代码的事,优雅是一辈子的事w~
T2
题面
左右横跳
时间限制:5秒 内存限制:256M
题目描述
小可在一个n个格子的场地左右横跳,每个格子都有一个权值,第 i 个格子的权值为ai 。
小可一开始在 1 位置,面向右侧,然后他要进行k次跳跃。
每次跳跃,小可都可以先他面向的方向,跳任意步,当然也可以跳0步,跳完之后立即转身。
也就是说如果这一次他向左跳,那么他下一次就会向右跳。
如果他从x格子跳到了y格子,那么小可的得分就会累加上x格子到y格子之间所有格子的权值之和,包括x和y。注意在k次跳跃的过程中,小可不可以跳出场地。
一开始小可的得分为 0,且任意时刻都要保证小可的得分非负。
请求出小可至多跳跃k次之和,得到的最大得分。
输入描述
第一行:输入一个正整数T,表示多组输入组数。
对于每组数据:
第一行:输入两个正整数 n,k,分别代表格子长度,和小可最多的跳跃次数。
接下来一行:输入n个整数 ,表示格子的权值 ai 。
输出描述
请求出小可至多跳跃k次之和,得到的最大得分。
输入样例
2
3 4
10 -100 80
15 400000000
-100 1 2 3 4 5 6 7 8 9 10 11 12 13 14
输出样例
90
41999999900
样例解释
第一次跳跃:向右跳。注意到若从 1 跳到 2,则得分变为 0 + (10) + (−100) = −90,为负,不符合题意,同理不能从 1 跳到 3,所以只能从 1 跳 0 步,还是待在 1,分数变为 10
第二次跳跃:向左跳。发现 1 的左边没有格子了,所以还是只能待在 1,分数变 20
第三次跳跃:向右跳。从 1 直接跳到 3,分数变为 10。
第四次跳跃:向左跳。从 3 跳到 3(原地不动),分数变为 90。
数据描述
对于20%的数据:n,k≤10
对于50%的数据:n,k≤1000
对于100%的数据:1≤T≤100,0≤∣ai∣≤10e5,1≤n≤1000,0≤k≤10e9
初次思路
爆搜拿20分
复盘思路
爆搜拿20分,逆天dp看不懂一点
T4
题面
循环排列
时间限制:1秒 内存限制:1024M
题目描述
你有一个长度为n的排列 a1,a2..an。 你需要对它进行 q 次操作:每次给定 l,r,k,代表把区间a[l,r] 循环右移 k 次。 每次移动过后,你需要回答整个排列是否存在三元上升子序列。 形式化地,即是否存在 i<j<k,使得 ai <aj< ak。
输入描述
第一行一个整数 n,代表排列长度。
接下来一行 n 个整数,表示序列 a。
接下来一行一个整数 q,代表询问次数。
接下来 q行,每行三个整数 l,r,k,表述如题。
输出描述
共 q 行。每次操作完毕后,若存在三元上升子序列,输出一行 ”YES“,否则输 出一行 ”NO“。
输入样例
7
7 5 6 3 4 2 1
5
4 5 1
2 5 2
2 6 3
2 7 3
1 7 4
输出样例
7
7 5 6 3 4 2 1
5
4 5 1
2 5 2
2 6 3
2 7 3
1 7 4
数据描述
对于 40% 的数据,n,q≤300。
对于 100% 的数据,1≤n,q≤120000。
保证每次询问的 l,r,k 都满足l≤r≤n,0≤k≤r−l+1。
初次思路
爆搜拿40分
爆搜思路:
1.模拟,更新数组
2.简单dp求最长上升子序列,>=3就YE5
code
#include<bits/stdc++.h>
using namespace std;
int n,q,l,r,k,a[100005],b[100005],c[100005],f[305];
int main(){
freopen("circulate.in","r",stdin);
freopen("circulate.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d%d%d",&l,&r,&k);
int mod=(r-l+1);
k%=mod;
int bruh=0,cruh=0;
for(int j=l;j<=r-k;j++){
b[++bruh]=a[j];
}
for(int j=r-k+1;j<=r;j++){
c[++cruh]=a[j];
}
for(int j=l;j<=l+cruh-1;j++){
a[j]=c[j-l+1];
}
for(int j=l+cruh;j<=r;j++){
a[j]=b[j-l-cruh+1];
}
int cnt=0,fl=0;
/*for(int j=1;j<=n;j++){
cout<<a[j]<<' ';
}*/
//cout<<endl;
for(int j=1;j<=n;j++){
f[j]=1;
}
for(int j=1;j<=n;j++){
for(int o=j-1;o>=1;o--){
if(a[o]<a[j]&&f[o]+1>f[j]){
f[j]=f[o]+1;
}
}
if(f[j]>=3){
fl=1;
break;
}
//cout<<f[j]<<' ';
}
//cout<<endl;
if(fl==1){
printf("YES\n");
}
else{
printf("NO\n");
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
/*效率是代码的事,优雅是一辈子的事w~
7
7 5 6 3 4 2 1
5
4 5 1
2 5 2
2 6 3
2 7 3
1 7 4*/
复盘思路
爆搜拿40分就够了,要什么自行车