A
如果爲偶數,那麼只需關心的最後一位的奇偶性即可。
如果爲奇數,那麼顯然和奇偶性相同。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
int a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
ll rdll(){
ll a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
const int N=100005;
int b,n,a[N],s;
int main(){
scanf("%d%d",&b,&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
if(b&1)for(int i=1;i<=n;i++)s=(s^a[i])&1;
else s=a[n]&1;
if(s)printf("odd");else printf("even");
return 0;
}
B
如果只能使用次膠帶,顯然在個點的個間隔中取個連接。
貪心,直接取間隔長度最小的即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
int a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
ll rdll(){
ll a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
const int N=100005;
int n,m,k,a[N],b[N];
ll s;
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);k=n-k;
for(int i=1;i< n;i++)a[i]=b[i+1]-b[i]-1;sort(a+1,a+n);
for(int i=1;i<=k;i++)s+=a[i];
printf("%I64d",s+n);
return 0;
}
C
看到公式,亂糟糟的,毫無頭緒?先打表找規律試試。
發現一個規律:
對於,設,則如果那麼。
如果怎麼辦?看起來沒規律?直接打表。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
int a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
ll rdll(){
ll a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
const int N=100005;
int n,t;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
if(n==1)printf("0\n");
else if(n==3)printf("1\n");
else if(n==7)printf("1\n");
else if(n==15)printf("5\n");
else if(n==31)printf("1\n");
else if(n==63)printf("21\n");
else if(n==127)printf("1\n");
else if(n==255)printf("85\n");
else if(n==511)printf("73\n");
else if(n==1023)printf("341\n");
else if(n==2047)printf("89\n");
else if(n==4095)printf("1365\n");
else if(n==8191)printf("1\n");
else if(n==16383)printf("5461\n");
else if(n==32767)printf("4681\n");
else if(n==65535)printf("21845\n");
else if(n==131071)printf("1\n");
else if(n==262143)printf("87381\n");
else if(n==524287)printf("1\n");
else if(n==1048575)printf("349525\n");
else if(n==2097151)printf("299593\n");
else if(n==4194303)printf("1398101\n");
else if(n==8388607)printf("178481\n");
else if(n==16777215)printf("5592405\n");
else if(n==33554431)printf("1082401\n");
else{
int a=1;
while(a<n)a=a<<1|1;
printf("%d\n",a);
}
}
return 0;
}
D
第一眼感覺貪心不可做?然後發現一個顯然的性質:
存在一個最優解,使對於任意有最多出現次。
接下來就可以無腦DP了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
int a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
ll rdll(){
ll a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
void chkmax(ll &a,ll b){if(a<b)a=b;}
const int N=1000005;
int n,m,a[N];
ll d[N][3][3],v[N][3][3],s;
int main(){
m=rd(),n=rd();while(m--)++a[rd()];
v[0][0][0]=1;
for(int i=1;i<=n;i++)for(int j=0;j<3;j++)for(int k=0;k<3;k++)for(int l=0;l<3;l++){
if(!v[i-1][j][k])continue;
if(i>n-2&&l)continue;
if(j+k+l<=a[i]){
chkmax(d[i][k][l],d[i-1][j][k]+(a[i]-j-k-l)/3+l);
v[i][k][l]=1;
}
}
printf("%I64d",d[n][0][0]);
return 0;
}
E
一個重要結論:對於一次在序列上的操作,的差分序列恰好交換了相鄰的2項。
所以檢查首尾和排序後的差分序列即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
int a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
ll rdll(){
ll a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
const int N=100005;
int n,a[N],b[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
if(a[1]!=b[1]||a[n]!=b[n]){printf("No");return 0;}
for(int i=1;i<n;i++)a[i]=a[i+1]-a[i],b[i]=b[i+1]-b[i];
sort(a+1,a+n),sort(b+1,b+n);
for(int i=1;i<n;i++)if(a[i]!=b[i]){printf("No");return 0;}
printf("Yes");
return 0;
}
F
不難發現,每次把當前節點移動到它的某個兒子時,
在子樹內的葉子節點到當前節點距離減少,其他葉子節點到當前節點距離增加。
由於圖的節點按DFS序編號,所以一個點子樹內葉子節點的編號都在某個區間內。
所以更換當前節點可以轉換爲有限次區間加。
至於詢問?離線,當DFS到某個點時回答從這個點出發的詢問,直接轉換爲區間最小值即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
int a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
ll rdll(){
ll a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
const int N=500005;
const ll inf=1000000000000000ll;
int n,q,cnt,st[N],ed[N];
vector<int>e[N],f[N],ql[N],qr[N],qi[N];
ll d[N],tre[N<<2],lazy[N<<2],ans[N],D;
void dfs(int v,int fa,ll dep){
if(e[v].size())d[v]=inf;else d[v]=dep;
st[v]=++cnt;
for(int i=0;i<(int)e[v].size();i++)dfs(e[v][i],v,dep+f[v][i]);
ed[v]= cnt;
}
void in();
#define mid ((l+r)>>1)
#define ls o<<1,l,mid
#define rs o<<1|1,mid+1,r
void pu(int o){tre[o]=min(tre[o<<1],tre[o<<1|1]);}
void pt(int o,ll v){tre[o]+=v,lazy[o]+=v;}
void pd(int o){pt(o<<1,lazy[o]),pt(o<<1|1,lazy[o]),lazy[o]=0;}
void build(int o,int l,int r){
if(l==r){tre[o]=d[l];return;}
build(ls),build(rs),pu(o);
}
void add(int o,int l,int r,int L,int R,ll v){
if(r<L||R<l)return;
if(L<=l&&r<=R){pt(o,v);return;}
pd(o),add(ls,L,R,v),add(rs,L,R,v),pu(o);
}
ll query(int o,int l,int r,int L,int R){
if(r<L||R<l)return inf;
if(L<=l&&r<=R)return tre[o];
pd(o);return min(query(ls,L,R),query(rs,L,R));
}
void solve(int v,int fa,int la){
D+=la,add(1,1,n,st[v],ed[v],-la-la);
for(int i=0;i<(int)qi[v].size();i++)ans[qi[v][i]]=D+query(1,1,n,ql[v][i],qr[v][i]);
for(int i=0;i<(int)e[v].size();i++)solve(e[v][i],v,f[v][i]);
D-=la,add(1,1,n,st[v],ed[v], la+la);
}
int main(){
in();
dfs(1,0,0);
build(1,1,n);
solve(1,0,0);
for(int i=1;i<=q;i++)printf("%I64d\n",ans[i]);
return 0;
}
void in(){
n=rd(),q=rd();
for(int i=2;i<=n;i++){
int x=rd(),y=rd();
e[x].push_back(i);
f[x].push_back(y);
}
for(int i=1;i<=q;i++){
int x=rd(),y=rd(),z=rd();
ql[x].push_back(y);
qr[x].push_back(z);
qi[x].push_back(i);
}
}
G
首先,可以把白點轉換爲無色點:
顯然在ABCD中雙方都無法獲勝。如果白子游戲開始先搶佔A,那麼黑子必須搶佔B,而C、D就不重要了。
現在考慮只有無色點的情況。
如果發生以下2種情況,那麼白子獲勝:
有度數的點 | 有度數且有個非葉子鄰居的點 |
---|---|
顯然,如果有個度數的點,那麼白子同樣獲勝。
那麼其他情況,看似一定打平。但有一個例外:
如果“骨頭”的中間部分長度爲奇數(這張圖中長度爲5),那麼白子同樣可以獲勝。
其他情況可以證明是平局。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
int a=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
return a;
}
const int N=2000005;
int t,n,m,d[N];
char c[N];
vector<int>e[N];
void add(int v,int u){e[v].push_back(u);e[u].push_back(v);}
void app(int v){e[++n].clear();add(v,n);}
int ac(){
int v=0;
for(int i=1;i<=n;i++)if(d[i]>3)return 4;
for(int i=1;i<=n;i++)if(d[i]>2){
++v;int x=0;
for(int j=0;j<d[i];j++)if(d[e[i][j]]!=1)++x;
if(x>1)return 3;
}
if(v>2)return 2;
return v==2&&(n&1);
}
int main(){
t=rd();
while(t--){
m=n=rd();
for(int i=1;i<=n;i++)e[i].clear();
for(int i=1;i<n;i++)add(rd(),rd());
scanf("%s",c+1);
for(int i=1;i<=m;i++)if(c[i]=='W'){app(i);int v=n;app(v);app(v);}
for(int i=1;i<=n;i++)d[i]=(int)e[i].size();
if(ac())puts("White");else puts("Draw");
}
return 0;
}
H
咕咕咕。