D Chocolate 博弈论,结论
H Matches 线段树求最大交
J Roulette 数学,概率论
K Subdivision 图论 细节
M Water 扩展欧几里得
n=1&&m=1时W必胜
n=1或者m=1时先手必胜。
n>1&&m>1时,假设W必胜,则W第一次消除时一定不会选择(n,m),所以他第一次消除时一定会剩下一部分。而先手完全可以事先达到这一状态,故先手必胜
#include
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
int main()
{
int n, m;
cin>>n>>m;
if(n==1&&m==1)
{
cout << "Walk Alone";
}
else
{
cout << "Kelin";
}
return 0;
}
只有如图一组i,j才能对答案产生贡献,贡献为线段交集的二倍。考虑将线段分组,上行与下行线段。都按照下端点从高到低进行排序。以访问上行 查询下行为例,每次把下端点大于等于本次下端点的下行线段加入线段树。线段树的下标是线段上端点的离散值。维护的内容有下端点的最小值与线段长度。我们求交集,只需要分为两类查询,一是上端点在本次上端点之内的,我们查询其长度最大值。上端点在本次上端点之外的,1我们查询其下端点最小值即可。对于查询到不相交的情况,贡献为正,故不会对答案产生影响。
#include
using namespace std;
using ll = long long;
int a[1000000+10],b[1000000+10],n,len;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
x=x*10+ch-'0',ch=getchar();
return x*f;
}
int lisan[1000000*2+10];
struct node
{
int down,up;
};
struct node s1[1000000+10],s2[1000000+10];
bool cmp(struct node x, struct node y)
{
return x.down>y.down;
}
ll maxx[1000000*2*4+10];
ll down[1000000*2*4+10];
void build(int root,int l,int r)
{
if(l==r)
{
maxx[root]=-1e15;
down[root]=1e15;
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
maxx[root]=max(maxx[root<<1],maxx[root<<1|1]);
down[root]=min(down[root<<1],down[root<<1|1]);
return ;
}
ll query(int root,int l,int r,int L,int R,int flag)
{
if(L<=l&&r<=R)
{
if(flag==1)
return maxx[root];
return down[root];
}
if(flag==1)
{
int mid=(l+r)>>1;
ll nowmax=-1e15;
if(L<=mid)
nowmax=max(nowmax,query(root<<1,l,mid,L,R,flag));
if(R>mid)
nowmax=max(nowmax,query(root<<1|1,mid+1,r,L,R,flag));
return nowmax;
}
else
{
int mid=(l+r)>>1;
ll nowmax=1e15;
if(L<=mid)
nowmax=min(nowmax,query(root<<1,l,mid,L,R,flag));
if(R>mid)
nowmax=min(nowmax,query(root<<1|1,mid+1,r,L,R,flag));
return nowmax;
}
}
void change(int root,int l,int r,int pos,ll nowmaxx,ll nowdown)
{
if(l==r)
{
maxx[root]=max(maxx[root],nowmaxx);
down[root]=min(down[root],nowdown);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)
change(root<<1,l,mid,pos,nowmaxx,nowdown);
else
change(root<<1|1,mid+1,r,pos,nowmaxx,nowdown);
maxx[root]=max(maxx[root<<1],maxx[root<<1|1]);
down[root]=min(down[root<<1],down[root<<1|1]);
}
int main()
{
cin>>n;
ll nowsum=0;
for(int i=1;i<=n;i++)
{
a[i]=read();
len++;
lisan[len]=a[i];
}
for(int i=1;i<=n;i++)
{
b[i]=read();
len++;
lisan[len]=b[i];
nowsum+=abs(a[i]-b[i]);
}
sort(lisan+1,lisan+1+len);
len=unique(lisan+1,lisan+1+len)-lisan-1;
int len1=0,len2=0;
for(int i=1;i<=n;i++)
{
if(a[i]=s2[i].down)
{
int nowpos=lower_bound(lisan+1,lisan+1+len,s1[r+1].up)-lisan;
change(1,1,len,nowpos,s1[r+1].up-s1[r+1].down,s1[r+1].down);
r++;
}
int nowpos=lower_bound(lisan+1,lisan+1+len,s2[i].up)-lisan;
ll nowmax=-1e15;
nowmax=query(1,1,len,1,nowpos,1);
ll nowmin=1e15;
nowmin=query(1,1,len,nowpos,len,2);
ll temp1=nowsum-2ll*nowmax;
ll temp2=nowsum-2ll*(s2[i].up-nowmin);
ans=min(ans,temp1);
ans=min(ans,temp2);
}
build(1,1,len);
l=1,r=0;
for(int i=1;i<=len1;i++)
{
while(r+1<=len2&&s2[r+1].down>=s1[i].down)
{
int nowpos=lower_bound(lisan+1,lisan+1+len,s2[r+1].up)-lisan;
change(1,1,len,nowpos,s2[r+1].up-s2[r+1].down,s2[r+1].down);
r++;
}
int nowpos=lower_bound(lisan+1,lisan+1+len,s1[i].up)-lisan;
ll nowmax=-1e15;
nowmax=query(1,1,len,1,nowpos,1);
ll nowmin=1e15;
nowmin=query(1,1,len,nowpos,len,2);
ll temp1=nowsum-2ll*nowmax;
ll temp2=nowsum-2ll*(s1[i].up-nowmin);
ans=min(ans,temp1);
ans=min(ans,temp2);
}
cout<
直接胜一场,钱总数+1,而后投标为1
败一场后胜一场,钱先-1后+2,总数+1,之后投标为1
败两场后胜一场,钱先减3后加4,总数+1,之后投标为1
所以得出结论,最终要想到达n+m,必须经过m轮连败(可为0)后胜。
设当前钱数是r,连败cnt次 (2^cnt)-1<=r 故可以求出cnt
则当前胜一场的,是1减去全败的概率=1-(1/2)^r。 直接遍历m次肯定超时,但是,考虑到相同cnt对应r是有一个很大区间的,且指数增长。同一区间的钱数r相同,胜的概率更相同。所以可以分段考虑。
#include
#include
#include
using namespace std;
typedef long long ll;
const ll N = 998244353;
ll qw(ll x, ll y)
{
ll res = 1;
for (; y; y >>= 1, x = (x * x )% N)
{
if (y & 1)
{
res = (res * x) % N;
}
}
return res;
}
ll inv(ll x)
{
return qw(x, N - 2);
}
ll LOG2(ll x)
{
int ans=0;
while((1ll<> n >> m;
ll ans = 1;
for (int i = n ; i <= n + m-1; )
{
int x = LOG2(i);//当前来说,如果要达到i,r不能超过这个
ll v = inv(qw(2,x))*(qw(2,x)-1) % N;//成功的概率
int num = min(m + n, (1 << (x + 1)) - 1); //从i到num,r都是相同的,
ans = (ans * qw(v, num - i) )% N;
i = num;
}
cout << ans;
return 0;
}
边权为1是本题突破点,意味着我们可以直接求出最短路上的边。先跑出BFS最短路,如果去影响最短路<=k的点。只有在一条路线末端没有其他点引出,且末端
否则,影响无法度量。考虑到,最短路
#include
using namespace std;
typedef long long ll;
const int N = 2e5 + 7;
typedef struct
{
int b, e;
} xinxi;
xinxi s[200000 * 2 + 10];
int f[200000 * 2 + 10], nex[200000 * 2 + 10], len = 2;
int vis[200000 * 2 + 10];
int book[200000 + 10], dis[200000 + 10];
void add(int x, int y)
{
s[len].b = x;
s[len].e = y;
nex[len] = f[x];
f[x] = len;
len++;
}
int x[200000 + 10], y[200000 + 10];
int du[200000 + 10];
int main()
{
memset(f, -1, sizeof(f));
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &x[i], &y[i]);
add(x[i], y[i]);
add(y[i], x[i]);
du[x[i]]++;
du[y[i]]++;
}
queue q;
for (int i = 1; i <= n; i++)
{
dis[i] = 2e9;
}
dis[1] = 0;
q.push(1);
book[1] = 1;
while (!q.empty())
{
int now = q.front();
q.pop();
int x = f[now];
while (x != -1)
{
int j = s[x].e;
if (book[j])
{
x = nex[x];
continue;
}
book[j] = 1;
dis[j] = dis[now] + 1;
vis[x] = vis[x ^ 1] = 1;
q.push(j);
x = nex[x];
}
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
if (dis[i] <= k)
ans++;
}
for (int i = 1; i <= m; i++)
{
int id = (i - 1) * 2 + 2;
// cout << vis[id] << " ";
if (vis[id])
{
continue;
}
if (dis[x[i]] < k)
{
ans += (k - dis[x[i]]);
}
if (dis[y[i]] < k)
{
ans += (k - dis[y[i]]);
}
}
for (int i = 2; i <= n; i++)
{
if (du[i] == 1 && dis[i] < k)
{
ans += (k - dis[i]);
}
}
cout << ans;
return 0;
}
数学题,扩欧,付队友皮哥代码
#include
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
ll res = exgcd(b, a % b, y, x);
y -= x * (a / b);
return res;
}
ll calc(ll a, ll b)
{
ll res = (abs(a) * 2 + abs(b) * 2);
if (a < 0 || b < 0)
res--;
return res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
{
ll A, B, k;
cin >> A >> B >> k;
if (A < B)
swap(A, B);
ll x, y;
ll gcd = exgcd(A, B, x, y);
if (k % gcd != 0)
{
cout << "-1\n";
continue;
}
x *= k / gcd, y *= k / gcd;
// cout << x << " " << y << "\n";
ll ta = A / gcd, tb = B / gcd;
// cout << k << " ";
ll x1 = (x % tb + tb) % tb, y1 = (k - x1 * A) / B;
ll x2 = x1 - tb, y2 = y1 + ta;
ll y3 = (y % ta + ta) % ta, x3 = (k - y3 * B) / A;
ll y4 = y3 - ta, x4 = x3 + tb;
ll ans = 1e18;
ll res1 = calc(x1, y1), res2 = calc(x2, y2), res3 = calc(x3, y3), res4 = calc(x4, y4);
// cout << x1 << " " << y1 << "\n";
// cout << x2 << " " << y2 << "\n";
// cout << x3 << " " << y3 << "\n";
// cout << x4 << " " << y4 << "\n";
ans = min(ans, res1);
ans = min(ans, res2);
ans = min(ans, res3);
ans = min(ans, res4);
if (ans != 1e18)
cout << ans << "\n";
else
cout << "-1\n";
}
return 0;
}