V-Diagram
和队友VP的时候看的英文题面,以为一个数组里面会有很多v图当时就想复杂了,导致后面思路都很混乱,贪心做的WA几发后才发现解法。
解析:
这个题目只有一个V图。
首先我们要找一个平均值最大的V图,那么我们就从最小的那个点出发。
因为从两边分别发散肯定是后一个数比前一个数大,所以平均值也就越来越大。
因此如果我们选的话,肯定得选完某一边或者两边都选。
这时我们可以思考一下,有没有只能把一边选完的情况,也就是比如我这时把右边选完,什么时候不选左边呢,左边会拉低我的平均值。但是因为这时左边肯定越后面值越大,对平均值贡献也越来越大,我要么左边全选要么只选一个做V图,同理右边也是。
假设最小值的点为x,最后答案的区间就是max({1,n},{1,x+1},{x-1,n})
codeforces的评测机有点不稳定,跑我这个代码是超时的,但是其他平台跑没问题
#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2)
#define debug(x) cout << "[debug] " #x << " = " << x << '\n';
#define ull unsigned long long
#define double long double
#define int long long
#define xiaowen ac
typedef pair<int, int> PII;
const int N=1e6+100;
const int inf = 0x3f3f3f3f3f3f3f3f;
double a[N];
double sum[N];
void solve() {
int n;
cin>>n;
sum[0]=0;
double minn=1e18;
int x=1;
for (int i=1;i<=n;i++){
cin>>a[i];
if (a[i]<minn){
minn=a[i];
x=i;
}
sum[i]=sum[i-1]+a[i];
}
double maxx;
maxx=max({(sum[n]-sum[0])*1.0/n,(sum[x+1]-sum[0])*1.0/(x+1),(sum[n]-sum[x-2])*1.0/(n-x+2)});
cout << fixed << setprecision(18) << maxx << '\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T;
T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
J. Mysterious Tree
当时队友在写M,帮不上什么忙,发现题目有个树,看了一下有思路,就单开了这道题。
解析:
我们可以从菊花图和链图的性质着手。
菊花也就是一个点与所有点都有边相连,链是一个点最多有两个点相连。
#那我们可以先找到相连的两个点,再去判断这两个点是否有一个点与除了连接的这个点之外的两个点相连,如果有则说明是菊花图,反之链图。
如何找到相连的两个点呢,题目有个要求提问次数不能超过(n+1)/2+3,那我们可以{1,2},{3,4}…{n-1,n}这样询问。
因为如果有菊花图,肯定能找到相连的两个点,反之就是链图。
但是要注意如果n是奇数,比如n=5,那么只能找到{1,2},{3,4}这时候要特判询问{4,5},判断5会不会是菊花图的点。这样询问次数就是(n+1)/2。
比如我这是找到{4,5},这时候就可以判断{4,1}是否有边,如果有则继续判断{4,2},遇到没有则中止,肯定说明这个点不会是菊花图的点。
这样询问次数正好最多是(n+1)/2+3。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5+100;
const int mod=998244353;
int a[N];
void solved() {
int n;
cin>>n;
int a=0,b=0;
for (int i=2; i<=n; i+=2) {
cout<<"? "<<i<<" "<<i-1<<endl;
int op;
cin>>op;
if (op==1) {
a=i,b=i-1;
break;
}
}
if (a==0&&b==0) {
if (n%2) {
cout<<"? "<<n-1<<" "<<n<<endl;
int op;
cin>>op;
if (op==1) {
a=n-1,b=n;
}
}
}
if (a==0&&b==0) {
cout<<"! 1"<<endl;
return;
}
int ans=1;
for (int i=1; i<=n; i++) {
if (b!=i&&a!=i) {
cout<<"? "<<a<<" "<<i<<endl;
int op;
cin>>op;
if (op==0) {
break;
} else {
ans+=1;
}
if (ans>2) {
cout<<"! 2"<<endl;
return;
}
}
}
ans=1;
for (int i=1; i<=n; i++) {
if (a!=i&&b!=i) {
cout<<"? "<<b<<" "<<i<<endl;
int op;
cin>>op;
if (op==0) {
break;
} else {
ans+=1;
}
if (ans>2) {
cout<<"! 2"<<endl;
return;
}
}
}
cout<<"! 1"<<endl;
}
// if (b!=)
signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0), cout.tie(0);
int t=1;
cin>>t;
while (t--) {
solved();
}
}
Operator Precedence
解析:
这道题可以从右边这个式子为突破口。
因为n范围为1e5,右边可以看成是连乘,(a2i-1+a2i+1)如果不是1的话,将会是指数级增长。
因此(a2i-1+a2i+1)只能是1,那我们就可以令a2i-1为2,a2i+1为-1,将左右化简,并令a1为1,解方程即可。
#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2)
#define debug(x) cout << "[debug] " #x << " = " << x << '\n';
#define double long double
#define int long long
#define xiaowen ac
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e6 + 7;
const int P = 131;
void solve()
{
int n;
cin>>n;
int a[2*n+10];
if(n==3) {
cout<<"1 -10 6 6 -10 1\n";
return;
}
a[1]=1,a[2*n]=3-n;
for(int i=2; i<2*n; i+=2) {
a[i]=2;
a[i+1]=-1;
}
for(int i=1; i<=2*n; i++) {
cout<<a[i]<<" \n"[i==2*n];
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T;
// T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
G. Snake Move
解析:
我们把复杂的问题逐步化简解决。
首先我们考虑没有蛇身,那么这道题就用bfs,相当于找到到达每一个点的最短距离。
接着我们考虑蛇身的影响,假如我们想要到达蛇身这个部位的时间,首先需要找到不考虑蛇身到达的最短时间,如果此时这个点还在蛇身上,那么我们可以通过缩短身子来到达这个点,也就是max(k-i+1-最短路的时间,0)。
当然可能正常的点也可以通过先到蛇身,再到这个点最短,这时可以用优先队列,谁的时间最短,谁在最前面
#include <bits/stdc++.h>
using namespace std;
#define int long long
int cnt;
const int N = 4e3 + 7;
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int a[N];
int dp[N][N];
string s[N];
map<pair<int,int>,int>mp;//标记这个点是否有蛇身
vector<pair<pair<int,int>,int>>v;//蛇身排第几段
queue<pair<pair<int,int>,int>>q;//正常跑BFS
priority_queue<pair<int,pair<int,int>>>p;//放蛇身
int vis[N][N];
int dx[]= {1,-1,0,0};
int dy[]= {0,0,1,-1};
int value[N][N];
unsigned long long ans=0;
int n,m,k;
void bfs() {
while (!p.empty()) {
int x=p.top().second.first;
int y=p.top().second.second;
vis[x][y]=1;
int cnt=-p.top().first+1;
p.pop();
for (int i=0; i<4; i++) {
int xx=x+dx[i];
int yy=y+dy[i];
if (xx<1||yy<1||xx>n||yy>m||s[xx][yy]=='#'||vis[xx][yy]) {
continue;
}
vis[xx][yy]=1;
if (value[xx][yy]) { //有蛇身
int num=0;
num=k-value[xx][yy]+1;
int mm=cnt+max(1ll*0,num-cnt);
ans+=mm*mm;
p.push({-mm,{xx,yy}});//放负的,小值排前面
} else {
ans+=cnt*cnt;
p.push({-cnt,{xx,yy}});
}
}
}
}
void solved() {
cin>>n>>m>>k;
for (int i=1; i<=k; i++) {
int a,b;
cin>>a>>b;
value[a][b]=i;
if (i==1) {
p.push({0,{a,b}});
}
}
for (int i=1; i<=n; i++) {
cin>>s[i];
s[i]=' '+s[i];
}
bfs();
cout<<ans<<"\n";
}
signed main() {
IOS
int t=1;
// cin>>t;
while (t--) {
solved();
}
}
H. Sugar Sweet II
解析:
因为题目中一个小朋友是否有额外的糖果会受到另一个小朋友糖果数量的制约。
有明显的关系我们可以用基环树来解决。
首先考虑建边如果ai<abi,那么是必然发生事件,那我们可以以ai为根,如果ai<abi+wbi,就说明可以建这条边,事件i发生只会发生在bi发生之后,如果ai>abi,那么这条边是必然不发生事件。
而且这样建边有个好处,就是不可能存在环,不用自己去拆环了。
这样每一个事件发生概率就是1/Li!,其中Li为基环树上i到必然发生祖先事件的距离,要想ai发生,那么中间这段顺序必须按这样发生,概率也就是这几种的排列的一种。
#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2)
#define debug(x) cout << "[debug] " #x << " = " << x << '\n';
#define double long double
#define int long long
#define xiaowen ac
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e6 + 7;
const int mod =1e9+7;
int n;
int a[N];
int b[N];
int w[N];
int nf[N];
bool vis[N];//标记跑过没有
int m[N];
vector<pair<int,int>>e[N];
vector<pair<int,int>>g[N];
int quick_pow(int a,int b) {
int ans=1;
while(b) {
if (b&1) {
ans=ans*a%mod;
}
a=a*a%mod;
b>>=1;
}
return ans%mod;
}
void dfs(int xx,int cnt) {
for (auto it:e[xx]) {
int v=it.first;
int i=it.second;
m[v]=(a[v]%mod+nf[cnt]*w[v]%mod)%mod;
dfs(v,cnt+1);
}
}
void solve() {
int n;
cin>>n;
for (int i=1; i<=n; i++) {
cin>>a[i];
e[i].clear();
vis[i]=0;
m[i]=a[i];
}
for (int i=1; i<=n; i++) {
cin>>b[i];
}
for (int i=1; i<=n; i++) {
cin>>w[i];
}
map<int,int>mp;
vector<int>v;
for (int i=1; i<=n; i++) {
if (i==b[i]) {
continue;
}
if (a[i]<a[b[i]]) {
v.push_back(i);
mp[b[i]]=1;
} else if (a[i]<a[b[i]]+w[b[i]]) {
e[b[i]].push_back({i,0});
}
}
for (auto x:v) {
m[x]=(a[x]%mod+nf[1]*w[x]%mod)%mod;
dfs(x,2);
}
for (int i=1; i<=n; i++) {
cout<<m[i]<<" ";
}
cout<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T;
// T = 1;
cin >> T;
nf[0]=1;
for (int i=1; i<=1e6; i++) {
nf[i]=nf[i-1]*quick_pow(i,mod-2)%mod;
}
while (T--) {
solve();
}
return 0;
}